Skip to content

Commit

Permalink
Implement moment deletion
Browse files Browse the repository at this point in the history
  • Loading branch information
ledyba committed Jul 23, 2021
1 parent f84c77d commit feb6077
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 24 deletions.
6 changes: 4 additions & 2 deletions _assets/templates/ura/edit.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
<option value="甚三紅もみじ" {{#if (eq author "甚三紅もみじ")}}selected{{/if}}>甚三紅もみじ</option>
</select>
<div class="space"></div>
<button id="upload_button">&nbsp;&nbsp;</button>
<button id="upload">&nbsp;&nbsp;</button>
<div class="space"></div>
<button id="submit">SAVE</button>
<button id="save">SAVE</button>
<div style="width: 1em;"></div>
<button id="delete">DEL</button>
</div>
<textarea id="text">{{text}}</textarea>
</div>
Expand Down
72 changes: 53 additions & 19 deletions client/src/ura/editor/Editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,29 @@ export default class Editor {
private readonly date_: HTMLInputElement;
private readonly author_: HTMLSelectElement;
private readonly text_: HTMLTextAreaElement;
private readonly submit_: HTMLButtonElement;
private readonly save_: HTMLButtonElement;
private readonly delete_: HTMLButtonElement;
private originalDate_: string;
private readonly onChangeEventListener_: () => void;
private preview_: Preview | null;
private changeId_: NodeJS.Timeout | null;
private changeId_: number | null;
executePreviewUpdater_: () => void;
onSaveEventListener_: () => void;
constructor(title: HTMLInputElement, date: HTMLInputElement, author: HTMLSelectElement, text: HTMLTextAreaElement, submit: HTMLButtonElement) {
onDeleteEventListener_: () => void;
constructor(title: HTMLInputElement, date: HTMLInputElement, author: HTMLSelectElement, text: HTMLTextAreaElement, save: HTMLButtonElement, del: HTMLButtonElement) {
this.title_ = title;
this.date_ = date;
this.author_ = author;
this.text_ = text;
this.submit_ = submit;
this.save_ = save;
this.delete_ = del;
this.originalDate_ = date.value;
this.onChangeEventListener_ = this.onChange_.bind(this);
this.preview_ = null;
this.changeId_ = null;
this.executePreviewUpdater_ = this.executePreviewUpdate_.bind(this);
this.onSaveEventListener_ = this.onSave_.bind(this);
this.onDeleteEventListener_ = this.onDelete_.bind(this);
}

init(preview: Preview) {
Expand All @@ -33,7 +37,8 @@ export default class Editor {
this.title_.addEventListener('input', this.onChangeEventListener_);
this.date_.addEventListener('input', this.onChangeEventListener_);
this.author_.addEventListener('change', this.onChangeEventListener_);
this.submit_.addEventListener('click', this.onSaveEventListener_);
this.save_.addEventListener('click', this.onSaveEventListener_);
this.delete_.addEventListener('click', this.onDeleteEventListener_);
window.addEventListener('keypress', (event) => {
if (!(event.which === 115 && event.ctrlKey) && !(event.which === 19)) return true;
window.setTimeout(this.onSaveEventListener_, 0);
Expand All @@ -44,19 +49,19 @@ export default class Editor {
if(this.text_.value.length > 0 || this.title_.value.length > 0) {
this.onChange_();
}
this.submit_.disabled = true;
this.delete_.disabled = location.pathname === '/new';
this.save_.disabled = true;
}
/**
* @private
*/
onChange_() {
this.submit_.disabled = false;

private onChange_() {
this.save_.disabled = false;
if(!!this.changeId_) {
return;
}
this.changeId_ = setTimeout(this.executePreviewUpdater_, 500);
this.changeId_ = window.setTimeout(this.executePreviewUpdater_, 500);
}
makeMoment_(): protocol.Moment.Save.Request {

private makeMoment_(): protocol.Moment.Save.Request {
return {
title: this.title_.value,
originalDate: this.originalDate_.length > 0 ? this.originalDate_ : null,
Expand All @@ -65,10 +70,8 @@ export default class Editor {
text: this.text_.value
};
}
/**
* @private
*/
onSave_() {

private onSave_() {
this.changeId_ = null;
const moment = this.makeMoment_();
fetch('/save', {
Expand All @@ -79,14 +82,45 @@ export default class Editor {
'Content-Type': 'application/json'
}}).then(result => result.json())
.then((result: protocol.Moment.Save.Response) => {
this.submit_.disabled = true;
this.save_.disabled = true;
this.delete_.disabled = false;
this.preview_!.onChange(result.body);
this.date_.value = result.date;
this.originalDate_ = result.date;
history.replaceState(null, moment.title, result.path);
});
}
executePreviewUpdate_() {

private onDelete_() {
if (this.date_.value.length === 0) {
return;
}
if (!confirm('本当に削除する?')) {
return;
}
const req: protocol.Moment.Delete.Request = {
date: this.date_.value,
};
fetch('/delete', {
method: 'POST',
body: JSON.stringify(req),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}})
.then(async(r) => {
if (r.ok) {
return await r.json() as protocol.Moment.Delete.Response;
} else {
throw new Error(await r.text());
}
})
.then((reply: protocol.Moment.Delete.Response) => {
window.location.href = `/moments/${reply.year}`;
});
}

private executePreviewUpdate_() {
this.changeId_ = null;
const moment = this.makeMoment_();
fetch('/preview', {
Expand Down
7 changes: 4 additions & 3 deletions client/src/ura/editor/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@ function newEditor(): Editor {
const date = document.getElementById('date') as HTMLInputElement;
const author = document.getElementById('author') as HTMLSelectElement;
const text = document.getElementById('text') as HTMLTextAreaElement;
const submit = document.getElementById('submit') as HTMLButtonElement;
return new Editor(title, date, author, text, submit);
const save = document.getElementById('save') as HTMLButtonElement;
const del = document.getElementById('delete') as HTMLButtonElement;
return new Editor(title, date, author, text, save, del);
}

function newPreview(): Preview {
return new Preview(document.getElementById('preview') as HTMLDivElement);
}

function newUploader(): Uploader {
return new Uploader(document.body, document.getElementById('upload_button') as HTMLInputElement);
return new Uploader(document.body, document.getElementById('upload') as HTMLInputElement);
}

let editor: Editor | null = null;
Expand Down
8 changes: 8 additions & 0 deletions lib/src/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,12 @@ export namespace Moment {
bodyURL: string;
};
}
export namespace Delete {
export type Request = {
date: string;
};
export type Response = {
year: string;
};
}
}
11 changes: 11 additions & 0 deletions server/src/Server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ import UploadController from './controller/ura/UploadController';
import NewController from './controller/ura/NewController';
import EditController from './controller/ura/EditController';
import SaveController from './controller/ura/SaveController';
import DeleteController from './controller/ura/DeleteController';
import PreviewController from './controller/ura/PreviewController';
// Both Controllers
import EntityController, {EntityControllerInterface} from './controller/both/EntityController';
import dayjs from "dayjs";

type Handler<Interface extends RouteGenericInterface> =
(req: FastifyRequest<Interface>, reply: FastifyReply) => PromiseLike<void>;
Expand Down Expand Up @@ -123,6 +125,9 @@ class Server {
}
{ // (ura)/moments
const ura = await MomentListController.create(this.asset, this.shelf);
this.ura.get<MomentListControllerInterface>('/moments/', async (req, reply) => {
reply.redirect(303, `/moments/${dayjs().year()}`);
});
this.ura.get<MomentListControllerInterface>('/moments/:year', async (req, reply) => {
await ura.handle(req, reply);
});
Expand All @@ -139,6 +144,12 @@ class Server {
await ura.handle(req, reply);
});
}
{ // (ura)/delete
const ura = await DeleteController.create(this.shelf);
this.ura.post('/delete', async (req, reply) => {
await ura.handle(req, reply);
});
}
{ // (ura)/preview
const ura = await PreviewController.create(this.shelf);
this.ura.post('/preview', async (req, reply) => {
Expand Down
39 changes: 39 additions & 0 deletions server/src/controller/ura/DeleteController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import dayjs from 'dayjs';
import { FastifyReply, FastifyRequest } from 'fastify';

import * as protocol from 'lib/protocol';

import Shelf from '../../shelf/Shelf';
import {formatMomentPath, formatMomentTime, parseMomentTime} from '../../shelf/Moment';
import MomentRenderer from '../../renderer/MomentRenderer';

export default class DeleteController {
private readonly shelf: Shelf;
private readonly renderer: MomentRenderer;
constructor(shelf: Shelf) {
this.shelf = shelf;
this.renderer = new MomentRenderer(this.shelf);
}
static async create(shelf: Shelf): Promise<DeleteController> {
return new DeleteController(shelf);
}
async handle(req: FastifyRequest, reply: FastifyReply) {
const r = req.body as protocol.Moment.Delete.Request;
const timestamp = parseMomentTime(r.date)
const result = await this.shelf.deleteMoment(timestamp);
if (!result) {
reply
.type('plain/text')
.code(500)
.send('Moment not found!');
return;
}
const resp: protocol.Moment.Delete.Response = {
year: timestamp.year().toString(10),
};
reply
.type('application/json')
.code(200)
.send(resp);
}
}
8 changes: 8 additions & 0 deletions server/src/repo/Repo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,14 @@ timestamp=$1;
}
return null;
}
async deleteMoment(timestamp: dayjs.Dayjs): Promise<boolean> {
// language=PostgreSQL
const q=`
delete from moments where timestamp=$1;
`;
const r = await this.pool.query(q, [timestamp.toDate()]);
return r.status?.trim() === 'DELETE 1';
}
}

function decodeMoment(row: ResultRow): Moment {
Expand Down
4 changes: 4 additions & 0 deletions server/src/shelf/Shelf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ class Shelf {
async findMoment(timestamp: dayjs.Dayjs): Promise<Moment | null> {
return await this.repo.findMoment(timestamp);
}
async deleteMoment(timestamp: dayjs.Dayjs): Promise<boolean> {
return await this.repo.deleteMoment(timestamp);
}
private async makeMoment(req: protocol.Moment.Save.Request): Promise<Moment> {
let date = req.date;
let iconID: string | undefined = undefined;
Expand Down Expand Up @@ -179,6 +182,7 @@ class Shelf {
iconID: iconID,
};
}

}

export default Shelf;

0 comments on commit feb6077

Please sign in to comment.