Merge pull request #459 from shikorism/fetch

Fetch APIを使う
This commit is contained in:
shibafu 2020-08-06 23:14:28 +09:00 committed by GitHub
commit a52211758e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 169 additions and 98 deletions

4
.eslintrc.js vendored
View File

@ -19,10 +19,12 @@ module.exports = {
parser: '@typescript-eslint/parser', parser: '@typescript-eslint/parser',
sourceType: 'module', sourceType: 'module',
}, },
plugins: ['prettier', 'vue', '@typescript-eslint'], plugins: ['prettier', 'vue', '@typescript-eslint', 'jquery'],
rules: { rules: {
'@typescript-eslint/explicit-module-boundary-types': 0, '@typescript-eslint/explicit-module-boundary-types': 0,
'@typescript-eslint/no-explicit-any': 0, '@typescript-eslint/no-explicit-any': 0,
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }], '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
'jquery/no-ajax': 2,
'jquery/no-ajax-events': 2,
}, },
}; };

View File

@ -17,6 +17,7 @@
"@types/chart.js": "^2.9.22", "@types/chart.js": "^2.9.22",
"@types/jquery": "^3.3.38", "@types/jquery": "^3.3.38",
"@types/js-cookie": "^2.2.0", "@types/js-cookie": "^2.2.0",
"@types/qs": "^6.9.4",
"@typescript-eslint/eslint-plugin": "^3.1.0", "@typescript-eslint/eslint-plugin": "^3.1.0",
"@typescript-eslint/parser": "^3.1.0", "@typescript-eslint/parser": "^3.1.0",
"bootstrap": "^4.5.0", "bootstrap": "^4.5.0",
@ -26,6 +27,7 @@
"date-fns": "^1.30.1", "date-fns": "^1.30.1",
"eslint": "^7.2.0", "eslint": "^7.2.0",
"eslint-config-prettier": "^6.11.0", "eslint-config-prettier": "^6.11.0",
"eslint-plugin-jquery": "^1.5.1",
"eslint-plugin-prettier": "^3.1.4", "eslint-plugin-prettier": "^3.1.4",
"eslint-plugin-vue": "^6.2.2", "eslint-plugin-vue": "^6.2.2",
"husky": "^1.3.1", "husky": "^1.3.1",
@ -37,6 +39,7 @@
"open-iconic": "^1.1.1", "open-iconic": "^1.1.1",
"popper.js": "^1.14.7", "popper.js": "^1.14.7",
"prettier": "^2.0.5", "prettier": "^2.0.5",
"qs": "^6.9.4",
"resolve-url-loader": "^3.1.1", "resolve-url-loader": "^3.1.1",
"sass": "^1.26.8", "sass": "^1.26.8",
"sass-loader": "^7.1.0", "sass-loader": "^7.1.0",
@ -62,7 +65,7 @@
"stylelint --fix", "stylelint --fix",
"git add" "git add"
], ],
"*.{ts,js,vue}" : [ "*.{ts,js,vue}": [
"eslint --fix", "eslint --fix",
"git add" "git add"
], ],

View File

@ -1,5 +1,5 @@
import * as Cookies from 'js-cookie'; import * as Cookies from 'js-cookie';
import jqXHR = JQuery.jqXHR; import { fetchPostJson, fetchDeleteJson, ResponseError } from './fetch';
require('./bootstrap'); require('./bootstrap');
@ -41,57 +41,46 @@ $(() => {
const isLiked = $this.data('liked'); const isLiked = $this.data('liked');
if (isLiked) { if (isLiked) {
const callback = (data: any) => { 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.data('liked', false);
$this.find('.oi-heart').removeClass('text-danger'); $this.find('.oi-heart').removeClass('text-danger');
const count = data.ejaculation ? data.ejaculation.likes_count : 0; const count = data.ejaculation ? data.ejaculation.likes_count : 0;
$this.find('.like-count').text(count ? count : ''); $this.find('.like-count').text(count ? count : '');
};
$.ajax({
url: '/api/likes/' + encodeURIComponent(targetId),
method: 'delete',
type: 'json',
}) })
.then(callback) .catch((e) => {
.catch(function (xhr: jqXHR) { console.error(e);
if (xhr.status === 404) {
callback(JSON.parse(xhr.responseText));
return;
}
console.error(xhr);
alert('いいねを解除できませんでした。'); alert('いいねを解除できませんでした。');
}); });
} else { } else {
const callback = (data: any) => { 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.data('liked', true);
$this.find('.oi-heart').addClass('text-danger'); $this.find('.oi-heart').addClass('text-danger');
const count = data.ejaculation ? data.ejaculation.likes_count : 0; const count = data.ejaculation ? data.ejaculation.likes_count : 0;
$this.find('.like-count').text(count ? count : ''); $this.find('.like-count').text(count ? count : '');
};
$.ajax({
url: '/api/likes',
method: 'post',
type: 'json',
data: {
id: targetId,
},
}) })
.then(callback) .catch((e) => {
.catch(function (xhr: jqXHR) { if (e instanceof ResponseError && e.response.status === 401) {
if (xhr.status === 409) {
callback(JSON.parse(xhr.responseText));
return;
} else if (xhr.status === 401) {
alert('いいねするためにはログインしてください。'); alert('いいねするためにはログインしてください。');
return; return;
} }
console.error(xhr); console.error(e);
alert('いいねできませんでした。'); alert('いいねできませんでした。');
}); });
} }

View File

@ -1,17 +1,5 @@
// jQuery // jQuery
import './tissue'; import './tissue';
// Setup global request header
const token = document.head.querySelector<HTMLMetaElement>('meta[name="csrf-token"]');
if (!token) {
console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
} else {
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': token.content,
},
});
}
// Bootstrap // Bootstrap
import 'bootstrap'; import 'bootstrap';

View File

@ -1,6 +1,7 @@
import Vue from 'vue'; import Vue from 'vue';
import TagInput from './components/TagInput.vue'; import TagInput from './components/TagInput.vue';
import MetadataPreview from './components/MetadataPreview.vue'; import MetadataPreview from './components/MetadataPreview.vue';
import { fetchGet, ResponseError } from './fetch';
export const bus = new Vue({ name: 'EventBus' }); export const bus = new Vue({ name: 'EventBus' });
@ -47,19 +48,18 @@ new Vue({
fetchMetadata(url: string) { fetchMetadata(url: string) {
this.metadataLoadState = MetadataLoadState.Loading; this.metadataLoadState = MetadataLoadState.Loading;
$.ajax({ fetchGet('/api/checkin/card', { url })
url: '/api/checkin/card', .then((response) => {
method: 'get', if (!response.ok) {
type: 'json', throw new ResponseError(response);
data: { }
url, return response.json();
},
}) })
.then((data) => { .then((data) => {
this.metadata = data; this.metadata = data;
this.metadataLoadState = MetadataLoadState.Success; this.metadataLoadState = MetadataLoadState.Success;
}) })
.catch((_e) => { .catch(() => {
this.metadata = null; this.metadata = null;
this.metadataLoadState = MetadataLoadState.Failed; this.metadataLoadState = MetadataLoadState.Failed;
}); });

View File

@ -0,0 +1,71 @@
import { stringify } from 'qs';
const token = document.head.querySelector<HTMLMetaElement>('meta[name="csrf-token"]');
if (!token) {
console.error('CSRF token not found');
}
const headers = {
'X-CSRF-TOKEN': token?.content ?? '',
};
type QueryParams = { [key: string]: string };
const joinParamsToPath = (path: string, params: QueryParams) =>
Object.keys(params).length === 0 ? path : `${path}?${stringify(params)}`;
const fetchWrapper = (path: string, options: RequestInit = {}) =>
fetch(path, {
credentials: 'same-origin',
...options,
headers: { ...headers, ...options.headers },
});
const fetchWithJson = (path: string, body?: any, options: RequestInit = {}) =>
fetchWrapper(path, {
...options,
body: body && JSON.stringify(body),
headers: { 'Content-Type': 'application/json', ...options.headers },
});
const fetchWithForm = (path: string, body?: any, options: RequestInit = {}) =>
fetchWrapper(path, {
...options,
body: body && stringify(body),
headers: { 'Content-Type': 'application/x-www-form-urlencoded', ...options.headers },
});
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 = {}) =>
fetchWithJson(path, body, { method: 'POST', ...options });
export const fetchPostForm = (path: string, body?: any, options: RequestInit = {}) =>
fetchWithForm(path, body, { method: 'POST', ...options });
export const fetchPutJson = (path: string, body?: any, options: RequestInit = {}) =>
fetchWithJson(path, body, { method: 'PUT', ...options });
export const fetchPutForm = (path: string, body?: any, options: RequestInit = {}) =>
fetchWithForm(path, body, { method: 'PUT', ...options });
export const fetchDeleteJson = (path: string, body?: any, options: RequestInit = {}) =>
fetchWithJson(path, body, { method: 'DELETE', ...options });
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;
}
}

View File

@ -1,3 +1,5 @@
import { fetchGet } from './fetch';
(function ($) { (function ($) {
$.fn.linkCard = function (options) { $.fn.linkCard = function (options) {
const settings = $.extend( const settings = $.extend(
@ -9,14 +11,15 @@
return this.each(function () { return this.each(function () {
const $this = $(this); const $this = $(this);
$.ajax({
url: settings.endpoint, const url = $this.find('a').attr('href');
method: 'get', if (!url) {
type: 'json', return;
data: { }
url: $this.find('a').attr('href'),
}, fetchGet(settings.endpoint, { url })
}).then(function (data) { .then((response) => response.json())
.then((data) => {
const $metaColumn = $this.find('.col-12:last-of-type'); const $metaColumn = $this.find('.col-12:last-of-type');
const $imageColumn = $this.find('.col-12:first-of-type'); const $imageColumn = $this.find('.col-12:first-of-type');
const $title = $this.find('.card-title'); const $title = $this.find('.card-title');

View File

@ -849,6 +849,11 @@
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8" resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8"
integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw== integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==
"@types/qs@^6.9.4":
version "6.9.4"
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.4.tgz#a59e851c1ba16c0513ea123830dd639a0a15cb6a"
integrity sha512-+wYo+L6ZF6BMoEjtf8zB2esQsqdV6WsjRK/GP9WOgLPrq87PbNWgIxS76dS5uvl/QXtHGakZmwTznIfcPXcKlQ==
"@types/sizzle@*": "@types/sizzle@*":
version "2.3.2" version "2.3.2"
resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47" resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47"
@ -3030,6 +3035,11 @@ eslint-config-prettier@^6.11.0:
dependencies: dependencies:
get-stdin "^6.0.0" get-stdin "^6.0.0"
eslint-plugin-jquery@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-jquery/-/eslint-plugin-jquery-1.5.1.tgz#d6bac643acf9484ce76394e27e2b07baca06662e"
integrity sha512-L7v1eaK5t80C0lvUXPFP9MKnBOqPSKhCOYyzy4LZ0+iK+TJwN8S9gAkzzP1AOhypRIwA88HF6phQ9C7jnOpW8w==
eslint-plugin-prettier@^3.1.4: eslint-plugin-prettier@^3.1.4:
version "3.1.4" version "3.1.4"
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.4.tgz#168ab43154e2ea57db992a2cd097c828171f75c2" resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.4.tgz#168ab43154e2ea57db992a2cd097c828171f75c2"
@ -6684,6 +6694,11 @@ qs@6.7.0:
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
qs@^6.9.4:
version "6.9.4"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.4.tgz#9090b290d1f91728d3c22e54843ca44aea5ab687"
integrity sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==
querystring-es3@^0.2.0: querystring-es3@^0.2.0:
version "0.2.1" version "0.2.1"
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"