4
									
								
								.eslintrc.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.eslintrc.js
									
									
									
									
										vendored
									
									
								
							@@ -19,10 +19,12 @@ module.exports = {
 | 
			
		||||
        parser: '@typescript-eslint/parser',
 | 
			
		||||
        sourceType: 'module',
 | 
			
		||||
    },
 | 
			
		||||
    plugins: ['prettier', 'vue', '@typescript-eslint'],
 | 
			
		||||
    plugins: ['prettier', 'vue', '@typescript-eslint', 'jquery'],
 | 
			
		||||
    rules: {
 | 
			
		||||
        '@typescript-eslint/explicit-module-boundary-types': 0,
 | 
			
		||||
        '@typescript-eslint/no-explicit-any': 0,
 | 
			
		||||
        '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
 | 
			
		||||
        'jquery/no-ajax': 2,
 | 
			
		||||
        'jquery/no-ajax-events': 2,
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@
 | 
			
		||||
    "@types/chart.js": "^2.9.22",
 | 
			
		||||
    "@types/jquery": "^3.3.38",
 | 
			
		||||
    "@types/js-cookie": "^2.2.0",
 | 
			
		||||
    "@types/qs": "^6.9.4",
 | 
			
		||||
    "@typescript-eslint/eslint-plugin": "^3.1.0",
 | 
			
		||||
    "@typescript-eslint/parser": "^3.1.0",
 | 
			
		||||
    "bootstrap": "^4.5.0",
 | 
			
		||||
@@ -26,6 +27,7 @@
 | 
			
		||||
    "date-fns": "^1.30.1",
 | 
			
		||||
    "eslint": "^7.2.0",
 | 
			
		||||
    "eslint-config-prettier": "^6.11.0",
 | 
			
		||||
    "eslint-plugin-jquery": "^1.5.1",
 | 
			
		||||
    "eslint-plugin-prettier": "^3.1.4",
 | 
			
		||||
    "eslint-plugin-vue": "^6.2.2",
 | 
			
		||||
    "husky": "^1.3.1",
 | 
			
		||||
@@ -37,6 +39,7 @@
 | 
			
		||||
    "open-iconic": "^1.1.1",
 | 
			
		||||
    "popper.js": "^1.14.7",
 | 
			
		||||
    "prettier": "^2.0.5",
 | 
			
		||||
    "qs": "^6.9.4",
 | 
			
		||||
    "resolve-url-loader": "^3.1.1",
 | 
			
		||||
    "sass": "^1.26.8",
 | 
			
		||||
    "sass-loader": "^7.1.0",
 | 
			
		||||
 
 | 
			
		||||
@@ -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) => {
 | 
			
		||||
            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');
 | 
			
		||||
 | 
			
		||||
                    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;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    console.error(xhr);
 | 
			
		||||
                .catch((e) => {
 | 
			
		||||
                    console.error(e);
 | 
			
		||||
                    alert('いいねを解除できませんでした。');
 | 
			
		||||
                });
 | 
			
		||||
        } 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.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) {
 | 
			
		||||
                .catch((e) => {
 | 
			
		||||
                    if (e instanceof ResponseError && e.response.status === 401) {
 | 
			
		||||
                        alert('いいねするためにはログインしてください。');
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    console.error(xhr);
 | 
			
		||||
                    console.error(e);
 | 
			
		||||
                    alert('いいねできませんでした。');
 | 
			
		||||
                });
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,17 +1,5 @@
 | 
			
		||||
// jQuery
 | 
			
		||||
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
 | 
			
		||||
import 'bootstrap';
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import TagInput from './components/TagInput.vue';
 | 
			
		||||
import MetadataPreview from './components/MetadataPreview.vue';
 | 
			
		||||
import { fetchGet, ResponseError } from './fetch';
 | 
			
		||||
 | 
			
		||||
export const bus = new Vue({ name: 'EventBus' });
 | 
			
		||||
 | 
			
		||||
@@ -47,19 +48,18 @@ new Vue({
 | 
			
		||||
        fetchMetadata(url: string) {
 | 
			
		||||
            this.metadataLoadState = MetadataLoadState.Loading;
 | 
			
		||||
 | 
			
		||||
            $.ajax({
 | 
			
		||||
                url: '/api/checkin/card',
 | 
			
		||||
                method: 'get',
 | 
			
		||||
                type: 'json',
 | 
			
		||||
                data: {
 | 
			
		||||
                    url,
 | 
			
		||||
                },
 | 
			
		||||
            fetchGet('/api/checkin/card', { url })
 | 
			
		||||
                .then((response) => {
 | 
			
		||||
                    if (!response.ok) {
 | 
			
		||||
                        throw new ResponseError(response);
 | 
			
		||||
                    }
 | 
			
		||||
                    return response.json();
 | 
			
		||||
                })
 | 
			
		||||
                .then((data) => {
 | 
			
		||||
                    this.metadata = data;
 | 
			
		||||
                    this.metadataLoadState = MetadataLoadState.Success;
 | 
			
		||||
                })
 | 
			
		||||
                .catch((_e) => {
 | 
			
		||||
                .catch(() => {
 | 
			
		||||
                    this.metadata = null;
 | 
			
		||||
                    this.metadataLoadState = MetadataLoadState.Failed;
 | 
			
		||||
                });
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										71
									
								
								resources/assets/js/fetch.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								resources/assets/js/fetch.ts
									
									
									
									
									
										Normal 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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
import { fetchGet } from './fetch';
 | 
			
		||||
 | 
			
		||||
(function ($) {
 | 
			
		||||
    $.fn.linkCard = function (options) {
 | 
			
		||||
        const settings = $.extend(
 | 
			
		||||
@@ -9,14 +11,15 @@
 | 
			
		||||
 | 
			
		||||
        return this.each(function () {
 | 
			
		||||
            const $this = $(this);
 | 
			
		||||
            $.ajax({
 | 
			
		||||
                url: settings.endpoint,
 | 
			
		||||
                method: 'get',
 | 
			
		||||
                type: 'json',
 | 
			
		||||
                data: {
 | 
			
		||||
                    url: $this.find('a').attr('href'),
 | 
			
		||||
                },
 | 
			
		||||
            }).then(function (data) {
 | 
			
		||||
 | 
			
		||||
            const url = $this.find('a').attr('href');
 | 
			
		||||
            if (!url) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            fetchGet(settings.endpoint, { url })
 | 
			
		||||
                .then((response) => response.json())
 | 
			
		||||
                .then((data) => {
 | 
			
		||||
                    const $metaColumn = $this.find('.col-12:last-of-type');
 | 
			
		||||
                    const $imageColumn = $this.find('.col-12:first-of-type');
 | 
			
		||||
                    const $title = $this.find('.card-title');
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								yarn.lock
									
									
									
									
									
								
							@@ -849,6 +849,11 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8"
 | 
			
		||||
  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@*":
 | 
			
		||||
  version "2.3.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47"
 | 
			
		||||
@@ -3030,6 +3035,11 @@ eslint-config-prettier@^6.11.0:
 | 
			
		||||
  dependencies:
 | 
			
		||||
    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:
 | 
			
		||||
  version "3.1.4"
 | 
			
		||||
  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"
 | 
			
		||||
  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:
 | 
			
		||||
  version "0.2.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user