diff --git a/resources/assets/js/app.ts b/resources/assets/js/app.ts index 7267f57..1196678 100644 --- a/resources/assets/js/app.ts +++ b/resources/assets/js/app.ts @@ -1,5 +1,5 @@ import * as Cookies from 'js-cookie'; -import jqXHR = JQuery.jqXHR; +import { fetchPostJson, fetchDeleteJson, ResponseError } from './fetch'; require('./bootstrap'); @@ -41,57 +41,46 @@ $(() => { const isLiked = $this.data('liked'); if (isLiked) { - const callback = (data: any) => { - $this.data('liked', false); - $this.find('.oi-heart').removeClass('text-danger'); - - const count = data.ejaculation ? data.ejaculation.likes_count : 0; - $this.find('.like-count').text(count ? count : ''); - }; - - $.ajax({ - url: '/api/likes/' + encodeURIComponent(targetId), - method: 'delete', - type: 'json', - }) - .then(callback) - .catch(function (xhr: jqXHR) { - if (xhr.status === 404) { - callback(JSON.parse(xhr.responseText)); - return; + fetchDeleteJson(`/api/likes/${encodeURIComponent(targetId)}`) + .then((response) => { + if (response.status === 200 || response.status === 404) { + return response.json(); } + throw new ResponseError(response); + }) + .then((data) => { + $this.data('liked', false); + $this.find('.oi-heart').removeClass('text-danger'); - console.error(xhr); + const count = data.ejaculation ? data.ejaculation.likes_count : 0; + $this.find('.like-count').text(count ? count : ''); + }) + .catch((e) => { + console.error(e); alert('いいねを解除できませんでした。'); }); } else { - const callback = (data: any) => { - $this.data('liked', true); - $this.find('.oi-heart').addClass('text-danger'); + fetchPostJson('/api/likes', { id: targetId }) + .then((response) => { + if (response.status === 200 || response.status === 409) { + return response.json(); + } + throw new ResponseError(response); + }) + .then((data) => { + $this.data('liked', true); + $this.find('.oi-heart').addClass('text-danger'); - const count = data.ejaculation ? data.ejaculation.likes_count : 0; - $this.find('.like-count').text(count ? count : ''); - }; - - $.ajax({ - url: '/api/likes', - method: 'post', - type: 'json', - data: { - id: targetId, - }, - }) - .then(callback) - .catch(function (xhr: jqXHR) { - if (xhr.status === 409) { - callback(JSON.parse(xhr.responseText)); - return; - } else if (xhr.status === 401) { + const count = data.ejaculation ? data.ejaculation.likes_count : 0; + $this.find('.like-count').text(count ? count : ''); + }) + .catch((e) => { + if (e instanceof ResponseError && e.response.status === 401) { alert('いいねするためにはログインしてください。'); return; } - console.error(xhr); + console.error(e); alert('いいねできませんでした。'); }); } diff --git a/resources/assets/js/fetch.ts b/resources/assets/js/fetch.ts index d683e62..128aec4 100644 --- a/resources/assets/js/fetch.ts +++ b/resources/assets/js/fetch.ts @@ -17,41 +17,55 @@ const joinParamsToPath = (path: string, params: QueryParams) => const fetchWrapper = (path: string, options: RequestInit = {}) => fetch(path, { credentials: 'same-origin', - headers, + headers: { ...headers, ...options.headers }, ...options, }); -const fetchWithJson = (path: string, body: any, options: RequestInit = {}) => +const fetchWithJson = (path: string, body?: any, options: RequestInit = {}) => fetchWrapper(path, { - body: JSON.stringify(body), - headers: { 'Content-Type': 'application/json' }, + body: body && JSON.stringify(body), + headers: { 'Content-Type': 'application/json', ...options.headers }, ...options, }); -const fetchWithForm = (path: string, body: any, options: RequestInit = {}) => +const fetchWithForm = (path: string, body?: any, options: RequestInit = {}) => fetchWrapper(path, { - body: stringify(body), - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: body && stringify(body), + headers: { 'Content-Type': 'application/x-www-form-urlencoded', ...options.headers }, ...options, }); export const fetchGet = (path: string, params: QueryParams = {}, options: RequestInit = {}) => fetchWrapper(joinParamsToPath(path, params), { method: 'GET', ...options }); -export const fetchPostJson = (path: string, body: any, options: RequestInit = {}) => +export const fetchPostJson = (path: string, body?: any, options: RequestInit = {}) => fetchWithJson(path, body, { method: 'POST', ...options }); -export const fetchPostForm = (path: string, body: any, options: RequestInit = {}) => +export const fetchPostForm = (path: string, body?: any, options: RequestInit = {}) => fetchWithForm(path, body, { method: 'POST', ...options }); -export const fetchPutJson = (path: string, body: any, options: RequestInit = {}) => +export const fetchPutJson = (path: string, body?: any, options: RequestInit = {}) => fetchWithJson(path, body, { method: 'PUT', ...options }); -export const fetchPutForm = (path: string, body: any, options: RequestInit = {}) => +export const fetchPutForm = (path: string, body?: any, options: RequestInit = {}) => fetchWithForm(path, body, { method: 'PUT', ...options }); -export const fetchDeleteJson = (path: string, body: any, options: RequestInit = {}) => +export const fetchDeleteJson = (path: string, body?: any, options: RequestInit = {}) => fetchWithJson(path, body, { method: 'DELETE', ...options }); -export const fetchDeleteForm = (path: string, body: any, options: RequestInit = {}) => +export const fetchDeleteForm = (path: string, body?: any, options: RequestInit = {}) => fetchWithForm(path, body, { method: 'DELETE', ...options }); + +export class ResponseError extends Error { + response: Response; + + constructor(response: Response, ...rest: any) { + super(...rest); + if (Error.captureStackTrace) { + Error.captureStackTrace(this, ResponseError); + } + + this.name = 'ResponseError'; + this.response = response; + } +}