Author SHA1 Message Date
eai04191 0522270c1c 引数のheaderとstatusを使用する 2019-08-22 17:46:37 +09:00
eai04191 9bd6b23c05 複数のresponseを受けられるように 2019-08-22 17:43:19 +09:00
shibafuandGitHub 0e3878a808 Merge pull request #257 from eai04191/feature/test-pixiv--this-item-has-been-deleted--very-sad
PixivResolver 作品削除に伴うテスト差し替え
2019-08-20 22:19:15 +09:00
shibafu 4c0b245574 Merge branch 'feature/249-install-dom-crawlar' into develop 2019-08-20 20:52:57 +09:00
shibafu 0a0047c4c3 symfony/css-selector を依存関係に追加
refs #249
2019-08-20 20:52:12 +09:00
shibafuandGitHub 831d1668ef Merge pull request #258 from shikorism/feature/249-install-dom-crawlar
symfony/dom-crawler を依存関係に追加
2019-08-20 20:50:10 +09:00
shibafu 51c8199283 symfony/dom-crawler を依存関係に追加
refs #249
2019-08-20 20:46:28 +09:00
shibafuandGitHub 78a1bdfb30 Merge pull request #254 from shikorism/feature/php-7.3
DockerfileおよびCI環境をPHP 7.3にアップデート
2019-08-20 20:44:39 +09:00
eai04191 ceff57f9f6 作品削除に伴いテスト差し替え 2019-08-20 11:31:50 +09:00
shibafu 58ae1bc1c1 DockerfileおよびCI環境をPHP 7.3にアップデート 2019-08-13 17:04:06 +09:00
shibafuandGitHub f7c9e83b12 Merge pull request #251 from shikorism/schedule-ci
定期的に実際のリクエストを伴うMetadataResolverのテストを実行する
2019-08-10 12:36:31 +09:00
shibafu a1850b666b 定期テストではMetadataResolverのテストのみを実行する 2019-08-10 12:18:36 +09:00
shibafuandGitHub dddb47f68a Merge pull request #248 from eai04191/feature/unknown-territory
Add XtubeResolver
2019-08-08 00:26:17 +09:00
shibafuandGitHub 1b2b043be2 Merge pull request #246 from shikorism/feature/monthly-graph-term
月間チェックイングラフの表示期間を選択可能にする
2019-08-08 00:23:15 +09:00
shibafu c535153e1f 定期的に実際のリクエストを伴うMetadataResolverのテストを実行する 2019-08-08 00:14:59 +09:00
eai04191 830de3a5e3 Add XtubeResolver 2019-08-06 22:44:38 +09:00
shibafu ab46117138 対象年の表記を yyyy年 にした 2019-08-04 01:33:54 +09:00
shibafu 3c2fec21a0 月間チェックイングラフの対象年を切り替えられるようにした 2019-08-04 01:31:53 +09:00
shibafu 370d1cc01b 月間グラフのデータ整形をクライアントサイドでやらせる 2019-08-04 00:57:35 +09:00
shibafuandGitHub 5517cd5fab Merge pull request #244 from shikorism/prestissimo
prestissimoのインストールをDockerfileに含める
2019-08-03 17:17:34 +09:00
shibafuandGitHub 3c083a7c60 Merge pull request #243 from shikorism/fix/235-tora
ToranoanaResolverで、OGP画像の代わりになりそうなサムネ画像を取得する
2019-08-03 17:06:56 +09:00
shibafu fa6b8b87af READMEからprestissimoのインストールを削除 2019-08-03 01:00:15 +09:00
shibafu d290bf4107 prestissimoを予めインストールしておく 2019-08-03 00:59:53 +09:00
shibafu b274c6bc40 Cookieを付与しなくてもデータを取得できたので、付与しないように変更 2019-08-03 00:26:12 +09:00
shibafu 018532f01f ToranoanaResolverのテストを追加 2019-08-03 00:16:21 +09:00
shibafu e4ef935dd2 OGP画像が全て代替イメージになっていたため、サムネイルのスクレイピングを実施 2019-08-02 23:48:56 +09:00
shibafuandGitHub fb84a1d416 Merge pull request #241 from shikorism/dependabot/npm_and_yarn/lodash-4.17.15
Bump lodash from 4.17.11 to 4.17.15
2019-07-25 20:17:24 +09:00
dependabot[bot]andGitHub 358580a15e Bump lodash from 4.17.11 to 4.17.15
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.11 to 4.17.15.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.11...4.17.15)

Signed-off-by: dependabot[bot] <[email protected]>
2019-07-25 11:12:45 +00:00
shibafuandGitHub 000b89f380 Merge pull request #237 from eai04191/feature/card-round-fix
カードの角丸を修正
2019-07-14 01:06:51 +09:00
shibafuandGitHub c5cbad4475 Merge pull request #238 from shikorism/feature/disable-inserted-tag
入力済みのタグ候補の背景色を変更する
2019-07-14 01:06:02 +09:00
shibafu f4abb08921 編集モードの初期表示時に入力済みタグが反映されていなかったので、現在のタグを配信させるイベントを定義 2019-07-04 22:34:28 +09:00
shibafu 38eb0348f9 入力済みのタグ候補の背景色を変更する 2019-07-04 22:33:28 +09:00
eai04191 4f5595dae0 vue側も追従 2019-07-04 21:28:54 +09:00
eai04191 733e97bc58 card-img類削除、.cardにoverflow付与 2019-07-04 21:28:26 +09:00
shibafuandeai04191 c4768ded38 Revert "Merge pull request #138 from eai04191/feature/bootstrap-custom"
This reverts commit 077731495c.
2019-07-04 20:58:59 +09:00
eai04191 598d27f6b8 Revert "Merge pull request #153 from shikorism/fix/140"
This reverts commit d044b6db20.
2019-07-04 20:55:33 +09:00
35 changed files with 14102 additions and 318 deletions
+94 -9
View File
@@ -1,9 +1,9 @@
version: 2 version: 2.1
jobs: executors:
build: build:
docker: docker:
- image: circleci/php:7.1-node-browsers - image: circleci/php:7.3-node-browsers
environment: environment:
APP_DEBUG: true APP_DEBUG: true
APP_ENV: testing APP_ENV: testing
@@ -17,38 +17,75 @@ jobs:
POSTGRES_DB: tissue POSTGRES_DB: tissue
POSTGRES_USER: tissue POSTGRES_USER: tissue
POSTGRES_PASSWORD: tissue POSTGRES_PASSWORD: tissue
commands:
initialize:
steps: steps:
- checkout - checkout
- run: sudo apt update - run: sudo apt update
- run: sudo apt install -y libpq-dev - run: sudo apt install -y libpq-dev
- run: sudo docker-php-ext-install zip - run: sudo docker-php-ext-install zip
- run: sudo docker-php-ext-install pdo_pgsql - run: sudo docker-php-ext-install pdo_pgsql
restore_composer:
steps:
- restore_cache: - restore_cache:
keys: keys:
- v1-dependencies-{{ checksum "composer.json" }} - v1-dependencies-{{ checksum "composer.json" }}
- v1-dependencies- - v1-dependencies-
- run: composer install -n --prefer-dist save_composer:
steps:
- save_cache: - save_cache:
key: v1-dependencies-{{ checksum "composer.json" }} key: v1-dependencies-{{ checksum "composer.json" }}
paths: paths:
- ./vendor - ./vendor
restore_npm:
steps:
- restore_cache: - restore_cache:
keys: keys:
- v1-dependencies-{{ checksum "package.json" }} - v1-dependencies-{{ checksum "package.json" }}
- v1-dependencies- - v1-dependencies-
- run: yarn install save_npm:
steps:
- save_cache: - save_cache:
key: v1-dependencies-{{ checksum "package.json" }} key: v1-dependencies-{{ checksum "package.json" }}
paths: paths:
- ./node_modules - ./node_modules
- ~/.yarn - ~/.yarn
- run: php artisan migrate jobs:
build:
executor: build
steps:
- initialize
- restore_composer
- run: composer install -n --prefer-dist
- save_composer
- restore_npm
- run: yarn install
- save_npm
- run: yarn run prod - run: yarn run prod
- persist_to_workspace:
root: .
paths:
- public
test:
executor: build
steps:
- initialize
- restore_composer
- restore_npm
- attach_workspace:
at: .
- run: php artisan migrate
# Run linter # Run linter
- run: - run:
command: | command: |
@@ -79,3 +116,51 @@ jobs:
- run: - run:
command: bash <(curl -s https://codecov.io/bash) -f /tmp/phpunit/coverage.xml command: bash <(curl -s https://codecov.io/bash) -f /tmp/phpunit/coverage.xml
when: always when: always
test_resolver:
executor: build
environment:
TEST_USE_HTTP_MOCK: false
steps:
- initialize
- restore_composer
- attach_workspace:
at: .
- run: php artisan migrate
# Run unit test
- run:
command: |
mkdir -p /tmp/phpunit
./vendor/bin/phpunit --testsuite MetadataResolver --log-junit /tmp/phpunit/phpunit.xml --coverage-clover=/tmp/phpunit/coverage.xml
when: always
- store_test_results:
path: /tmp/phpunit
- store_artifacts:
path: /tmp/phpunit/coverage.xml
workflows:
version: 2.1
test:
jobs:
- build
- test:
requires:
- build
scheduled_resolver_test:
triggers:
- schedule:
cron: "4 0 * * 1"
filters:
branches:
only:
- develop
jobs:
- build
- test_resolver:
requires:
- build
+2 -1
View File
@@ -1,6 +1,6 @@
FROM node:10-jessie as node FROM node:10-jessie as node
FROM php:7.1-apache FROM php:7.3-apache
ENV APACHE_DOCUMENT_ROOT /var/www/html/public ENV APACHE_DOCUMENT_ROOT /var/www/html/public
@@ -10,6 +10,7 @@ RUN apt-get update \
&& pecl install xdebug \ && pecl install xdebug \
&& curl -sS https://getcomposer.org/installer | php \ && curl -sS https://getcomposer.org/installer | php \
&& mv composer.phar /usr/local/bin/composer \ && mv composer.phar /usr/local/bin/composer \
&& composer global require hirak/prestissimo \
&& sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf \ && sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf \
&& sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf \ && sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf \
&& a2enmod rewrite && a2enmod rewrite
-1
View File
@@ -36,7 +36,6 @@ docker-compose up -d
4. Composer と yarn を使い必要なライブラリをインストールします。 4. Composer と yarn を使い必要なライブラリをインストールします。
``` ```
docker-compose exec web composer global require hirak/prestissimo
docker-compose exec web composer install docker-compose exec web composer install
docker-compose exec web yarn install docker-compose exec web yarn install
``` ```
+2 -12
View File
@@ -109,13 +109,6 @@ SQL
} }
} }
// 月間グラフ用の配列初期化
$month = Carbon::now()->firstOfMonth()->subMonth(11); // 直近12ヶ月
for ($i = 0; $i < 12; $i++) {
$monthlySum[$month->format('Y/m')] = 0;
$month->addMonth();
}
foreach ($groupByDay as $data) { foreach ($groupByDay as $data) {
$date = Carbon::createFromFormat('Y/m/d', $data->date); $date = Carbon::createFromFormat('Y/m/d', $data->date);
$yearAndMonth = $date->format('Y/m'); $yearAndMonth = $date->format('Y/m');
@@ -123,9 +116,7 @@ SQL
$dailySum[$date->timestamp] = $data->count; $dailySum[$date->timestamp] = $data->count;
$yearlySum[$date->year] += $data->count; $yearlySum[$date->year] += $data->count;
$dowSum[$date->dayOfWeek] += $data->count; $dowSum[$date->dayOfWeek] += $data->count;
if (isset($monthlySum[$yearAndMonth])) { $monthlySum[$yearAndMonth] = ($monthlySum[$yearAndMonth] ?? 0) + $data->count;
$monthlySum[$yearAndMonth] += $data->count;
}
} }
foreach ($groupByHour as $data) { foreach ($groupByHour as $data) {
@@ -136,8 +127,7 @@ SQL
$graphData = [ $graphData = [
'dailySum' => $dailySum, 'dailySum' => $dailySum,
'dowSum' => $dowSum, 'dowSum' => $dowSum,
'monthlyKey' => array_keys($monthlySum), 'monthlySum' => $monthlySum,
'monthlySum' => array_values($monthlySum),
'yearlyKey' => array_keys($yearlySum), 'yearlyKey' => array_keys($yearlySum),
'yearlySum' => array_values($yearlySum), 'yearlySum' => array_values($yearlySum),
'hourlyKey' => array_keys($hourlySum), 'hourlyKey' => array_keys($hourlySum),
@@ -28,6 +28,7 @@ class MetadataResolver implements Resolver
'~www\.plurk\.com\/p\/.*~' => PlurkResolver::class, '~www\.plurk\.com\/p\/.*~' => PlurkResolver::class,
'~(adult\.)?contents\.fc2\.com\/article_search\.php\?id=\d+~' => FC2ContentsResolver::class, '~(adult\.)?contents\.fc2\.com\/article_search\.php\?id=\d+~' => FC2ContentsResolver::class,
'~store\.steampowered\.com/app/\d+~' => SteamResolver::class, '~store\.steampowered\.com/app/\d+~' => SteamResolver::class,
'~www\.xtube\.com/video-watch/.*-\d+$~'=> XtubeResolver::class,
]; ];
public $mimeTypes = [ public $mimeTypes = [
+12 -4
View File
@@ -24,11 +24,19 @@ class ToranoanaResolver implements Resolver
public function resolve(string $url): Metadata public function resolve(string $url): Metadata
{ {
$cookieJar = CookieJar::fromArray(['adflg' => '0'], 'ec.toranoana.jp'); $res = $this->client->get($url);
$res = $this->client->get($url, ['cookies' => $cookieJar]);
if ($res->getStatusCode() === 200) { if ($res->getStatusCode() === 200) {
return $this->ogpResolver->parse($res->getBody()); $metadata = $this->ogpResolver->parse($res->getBody());
$dom = new \DOMDocument();
@$dom->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8'));
$xpath = new \DOMXPath($dom);
$imgNode = $xpath->query('//*[@id="preview"]//img')->item(0);
if ($imgNode !== null) {
$metadata->image = $imgNode->getAttribute('src');
}
return $metadata;
} else { } else {
throw new \RuntimeException("{$res->getStatusCode()}: $url"); throw new \RuntimeException("{$res->getStatusCode()}: $url");
} }
+41
View File
@@ -0,0 +1,41 @@
<?php
namespace App\MetadataResolver;
use GuzzleHttp\Client;
class XtubeResolver implements Resolver
{
/**
* @var Client
*/
private $client;
public function __construct(Client $client)
{
$this->client = $client;
}
public function resolve(string $url): Metadata
{
if (preg_match('~www\.xtube\.com/video-watch/.*-(\d+)$~', $url, $matches) !== 1) {
throw new \RuntimeException("Unmatched URL Pattern: $url");
}
$videoid = $matches[1];
$res = $this->client->get('https://www.xtube.com/webmaster/api/getvideobyid?video_id=' . $videoid);
if ($res->getStatusCode() === 200) {
$data = json_decode($res->getBody()->getContents(), true);
$metadata = new Metadata();
$metadata->title = $data['title'] ?? '';
$metadata->description = strip_tags(str_replace('\n', PHP_EOL, html_entity_decode($data['description'] ?? '')));
$metadata->image = str_replace('eSuQ8f', 'eSK08f', $data['thumb'] ?? ''); // 300x169 to 300x210
$metadata->tags = array_values(array_unique($data['tags']));
return $metadata;
} else {
throw new \RuntimeException("{$res->getStatusCode()}: $url");
}
}
}
+3 -1
View File
@@ -13,7 +13,9 @@
"laravel/framework": "5.5.*", "laravel/framework": "5.5.*",
"laravel/tinker": "~1.0", "laravel/tinker": "~1.0",
"misd/linkify": "^1.1", "misd/linkify": "^1.1",
"staudenmeir/eloquent-eager-limit": "^1.0" "staudenmeir/eloquent-eager-limit": "^1.0",
"symfony/css-selector": "^4.3",
"symfony/dom-crawler": "^4.3"
}, },
"require-dev": { "require-dev": {
"barryvdh/laravel-debugbar": "^3.1", "barryvdh/laravel-debugbar": "^3.1",
Generated
+73 -11
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "665f6f5eb180a1295fb60303d2ea5051", "content-hash": "28abd730d4572663d10ae815393c73cd",
"packages": [ "packages": [
{ {
"name": "anhskohbo/no-captcha", "name": "anhskohbo/no-captcha",
@@ -2113,16 +2113,16 @@
}, },
{ {
"name": "symfony/css-selector", "name": "symfony/css-selector",
"version": "v4.2.1", "version": "v4.3.3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/css-selector.git", "url": "https://github.com/symfony/css-selector.git",
"reference": "aa9fa526ba1b2ec087ffdfb32753803d999fcfcd" "reference": "105c98bb0c5d8635bea056135304bd8edcc42b4d"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/css-selector/zipball/aa9fa526ba1b2ec087ffdfb32753803d999fcfcd", "url": "https://api.github.com/repos/symfony/css-selector/zipball/105c98bb0c5d8635bea056135304bd8edcc42b4d",
"reference": "aa9fa526ba1b2ec087ffdfb32753803d999fcfcd", "reference": "105c98bb0c5d8635bea056135304bd8edcc42b4d",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -2131,7 +2131,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "4.2-dev" "dev-master": "4.3-dev"
} }
}, },
"autoload": { "autoload": {
@@ -2147,14 +2147,14 @@
"MIT" "MIT"
], ],
"authors": [ "authors": [
{
"name": "Jean-François Simon",
"email": "[email protected]"
},
{ {
"name": "Fabien Potencier", "name": "Fabien Potencier",
"email": "[email protected]" "email": "[email protected]"
}, },
{
"name": "Jean-François Simon",
"email": "[email protected]"
},
{ {
"name": "Symfony Community", "name": "Symfony Community",
"homepage": "https://symfony.com/contributors" "homepage": "https://symfony.com/contributors"
@@ -2162,7 +2162,7 @@
], ],
"description": "Symfony CssSelector Component", "description": "Symfony CssSelector Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2018-11-11T19:52:12+00:00" "time": "2019-01-16T21:53:39+00:00"
}, },
{ {
"name": "symfony/debug", "name": "symfony/debug",
@@ -2220,6 +2220,67 @@
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2018-11-27T12:43:10+00:00" "time": "2018-11-27T12:43:10+00:00"
}, },
{
"name": "symfony/dom-crawler",
"version": "v4.3.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/dom-crawler.git",
"reference": "291397232a2eefb3347eaab9170409981eaad0e2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/291397232a2eefb3347eaab9170409981eaad0e2",
"reference": "291397232a2eefb3347eaab9170409981eaad0e2",
"shasum": ""
},
"require": {
"php": "^7.1.3",
"symfony/polyfill-ctype": "~1.8",
"symfony/polyfill-mbstring": "~1.0"
},
"conflict": {
"masterminds/html5": "<2.6"
},
"require-dev": {
"masterminds/html5": "^2.6",
"symfony/css-selector": "~3.4|~4.0"
},
"suggest": {
"symfony/css-selector": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.3-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\DomCrawler\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "[email protected]"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony DomCrawler Component",
"homepage": "https://symfony.com",
"time": "2019-06-13T11:03:18+00:00"
},
{ {
"name": "symfony/event-dispatcher", "name": "symfony/event-dispatcher",
"version": "v4.2.1", "version": "v4.2.1",
@@ -4888,6 +4949,7 @@
"mock", "mock",
"xunit" "xunit"
], ],
"abandoned": true,
"time": "2018-08-09T05:50:03+00:00" "time": "2018-08-09T05:50:03+00:00"
}, },
{ {
+1
View File
@@ -16,6 +16,7 @@
"cal-heatmap": "^3.3.10", "cal-heatmap": "^3.3.10",
"chart.js": "^2.7.1", "chart.js": "^2.7.1",
"cross-env": "^5.2.0", "cross-env": "^5.2.0",
"date-fns": "^1.30.1",
"husky": "^1.3.1", "husky": "^1.3.1",
"jquery": "^3.2.1", "jquery": "^3.2.1",
"js-cookie": "^2.2.0", "js-cookie": "^2.2.0",
+4
View File
@@ -16,6 +16,10 @@
<testsuite name="Unit"> <testsuite name="Unit">
<directory suffix="Test.php">./tests/Unit</directory> <directory suffix="Test.php">./tests/Unit</directory>
</testsuite> </testsuite>
<testsuite name="MetadataResolver">
<directory suffix="Test.php">./tests/Unit/MetadataResolver</directory>
</testsuite>
</testsuites> </testsuites>
<filter> <filter>
<whitelist processUncoveredFilesFromWhitelist="true"> <whitelist processUncoveredFilesFromWhitelist="true">
@@ -11,7 +11,7 @@
</div> </div>
<div v-else-if="state === MetadataLoadState.Success" class="row no-gutters"> <div v-else-if="state === MetadataLoadState.Success" class="row no-gutters">
<div v-if="hasImage" class="col-4 justify-content-center align-items-center"> <div v-if="hasImage" class="col-4 justify-content-center align-items-center">
<img :src="metadata.image" alt="Thumbnail" class="card-img-top-to-left bg-secondary"> <img :src="metadata.image" alt="Thumbnail" class="w-100 bg-secondary">
</div> </div>
<div :class="descClasses"> <div :class="descClasses">
<div class="card-body"> <div class="card-body">
@@ -20,8 +20,8 @@
<p class="card-text mb-2" style="font-size: small;">タグ候補<br><span class="text-secondary">(クリックするとタグ入力欄にコピーできます)</span></p> <p class="card-text mb-2" style="font-size: small;">タグ候補<br><span class="text-secondary">(クリックするとタグ入力欄にコピーできます)</span></p>
<ul class="list-inline d-inline"> <ul class="list-inline d-inline">
<li v-for="tag in suggestions" <li v-for="tag in suggestions"
class="list-inline-item badge badge-primary metadata-tag-item" :class="tagClasses(tag)"
@click="addTag(tag)"><span class="oi oi-tag"></span> {{ tag }}</li> @click="addTag(tag.name)"><span class="oi oi-tag"></span> {{ tag.name }}</li>
</ul> </ul>
</template> </template>
</div> </div>
@@ -54,6 +54,11 @@
}[], }[],
}; };
type Suggestion = {
name: string,
used: boolean,
}
@Component @Component
export default class MetadataPreview extends Vue { export default class MetadataPreview extends Vue {
@Prop() readonly state!: MetadataLoadState; @Prop() readonly state!: MetadataLoadState;
@@ -62,16 +67,38 @@
// for use in v-if // for use in v-if
private readonly MetadataLoadState = MetadataLoadState; private readonly MetadataLoadState = MetadataLoadState;
tags: string[] = [];
created() {
bus.$on("change-tag", (tags: string[]) => this.tags = tags);
bus.$emit("resend-tag");
}
addTag(tag: string) { addTag(tag: string) {
bus.$emit("add-tag", tag); bus.$emit("add-tag", tag);
} }
get suggestions() { tagClasses(s: Suggestion) {
return {
"list-inline-item": true,
"badge": true,
"badge-primary": !s.used,
"badge-secondary": s.used,
"metadata-tag-item": true,
};
}
get suggestions(): Suggestion[] {
if (this.metadata === null) { if (this.metadata === null) {
return []; return [];
} }
return this.metadata.tags.map(t => t.name); return this.metadata.tags.map(t => {
return {
name: t.name,
used: this.tags.indexOf(t.name) !== -1
};
});
} }
get hasImage() { get hasImage() {
@@ -90,10 +117,7 @@
<style lang="scss" scoped> <style lang="scss" scoped>
.link-card-mini { .link-card-mini {
$height: 150px; $height: 150px;
.row > div {
overflow: hidden; overflow: hidden;
}
.row > div:first-child { .row > div:first-child {
display: flex; display: flex;
+7 -1
View File
@@ -16,7 +16,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import {Vue, Component, Prop} from "vue-property-decorator"; import {Vue, Component, Prop, Watch} from "vue-property-decorator";
import {bus} from "../checkin"; import {bus} from "../checkin";
@Component @Component
@@ -31,6 +31,7 @@
created() { created() {
bus.$on("add-tag", (tag: string) => this.tags.indexOf(tag) === -1 && this.tags.push(tag)); bus.$on("add-tag", (tag: string) => this.tags.indexOf(tag) === -1 && this.tags.push(tag));
bus.$on("resend-tag", () => bus.$emit("change-tag", this.tags));
} }
onKeyDown(event: KeyboardEvent) { onKeyDown(event: KeyboardEvent) {
@@ -56,6 +57,11 @@
this.tags.splice(index, 1); this.tags.splice(index, 1);
} }
@Watch("tags")
onTagsChanged() {
bus.$emit("change-tag", this.tags);
}
get containerClass(): object { get containerClass(): object {
return { return {
"form-control": true, "form-control": true,
+54 -3
View File
@@ -1,9 +1,12 @@
import CalHeatMap from 'cal-heatmap'; import CalHeatMap from 'cal-heatmap';
import Chart from 'chart.js'; import Chart from 'chart.js';
import {addMonths, format, startOfMonth, subMonths} from 'date-fns';
const graphData = JSON.parse(document.getElementById('graph-data').textContent);
function createLineGraph(id, labels, data) { function createLineGraph(id, labels, data) {
const context = document.getElementById(id).getContext('2d'); const context = document.getElementById(id).getContext('2d');
new Chart(context, { return new Chart(context, {
type: 'line', type: 'line',
data: { data: {
labels: labels, labels: labels,
@@ -62,7 +65,22 @@ function createBarGraph(id, labels, data) {
}); });
} }
const graphData = JSON.parse(document.getElementById('graph-data').textContent); /**
* @param {Date} from
*/
function createMonthlyGraphData(from) {
const keys = [];
const values = [];
for (let i = 0; i < 12; i++) {
const current = addMonths(from, i);
const yearAndMonth = format(current, 'YYYY/MM');
keys.push(yearAndMonth);
values.push(graphData.monthlySum[yearAndMonth] || 0);
}
return {keys, values};
}
new CalHeatMap().init({ new CalHeatMap().init({
itemSelector: '#cal-heatmap', itemSelector: '#cal-heatmap',
@@ -76,7 +94,40 @@ new CalHeatMap().init({
legend: [1, 2, 3, 4] legend: [1, 2, 3, 4]
}); });
createLineGraph('monthly-graph', graphData.monthlyKey, graphData.monthlySum); // 直近1年の月間グラフのデータを準備
const monthlyTermFrom = subMonths(startOfMonth(new Date()), 11);
const {keys: monthlyKey, values: monthlySum} = createMonthlyGraphData(monthlyTermFrom);
const monthlyGraph = createLineGraph('monthly-graph', monthlyKey, monthlySum);
createLineGraph('yearly-graph', graphData.yearlyKey, graphData.yearlySum); createLineGraph('yearly-graph', graphData.yearlyKey, graphData.yearlySum);
createBarGraph('hourly-graph', graphData.hourlyKey, graphData.hourlySum); createBarGraph('hourly-graph', graphData.hourlyKey, graphData.hourlySum);
createBarGraph('dow-graph', ['日', '月', '火', '水', '木', '金', '土'], graphData.dowSum); createBarGraph('dow-graph', ['日', '月', '火', '水', '木', '金', '土'], graphData.dowSum);
// 月間グラフの期間セレクターを準備
const monthlyTermSelector = document.getElementById('monthly-term');
for (let year = monthlyTermFrom.getFullYear(); year <= new Date().getFullYear(); year++) {
const opt = document.createElement('option');
opt.setAttribute('value', year);
opt.textContent = `${year}`;
monthlyTermSelector.insertBefore(opt, monthlyTermSelector.firstChild);
}
if (monthlyTermSelector.children.length) {
monthlyTermSelector.selectedIndex = 0;
}
monthlyTermSelector.addEventListener('change', function (e) {
let monthlyTermFrom;
if (e.target.selectedIndex === 0) {
// 今年のデータを表示する時は、直近12ヶ月を表示
monthlyTermFrom = subMonths(startOfMonth(new Date()), 11);
} else {
// 過去のデータを表示する時は、選択年の1〜12月を表示
monthlyTermFrom = new Date(e.target.value, 0, 1);
}
const {keys, values} = createMonthlyGraphData(monthlyTermFrom);
monthlyGraph.data.labels = keys;
monthlyGraph.data.datasets[0].data = values;
monthlyGraph.update();
});
-19
View File
@@ -1,19 +0,0 @@
.card-img-left {
width: 100%;
@include border-left-radius($card-inner-border-radius);
}
.card-img-right {
width: 100%;
@include border-right-radius($card-inner-border-radius);
}
.card-img-top-to-left {
width: 100%;
@include media-breakpoint-down(md) {
@include border-top-radius($card-inner-border-radius);
}
@include media-breakpoint-up(lg) {
@include border-left-radius($card-inner-border-radius);
}
}
-1
View File
@@ -3,7 +3,6 @@ $primary: #e53fb1;
// Bootstrap // Bootstrap
@import "~bootstrap/scss/bootstrap"; @import "~bootstrap/scss/bootstrap";
@import "bootstrap-custom";
// Open Iconic // Open Iconic
@import "~open-iconic/font/css/open-iconic-bootstrap"; @import "~open-iconic/font/css/open-iconic-bootstrap";
+2
View File
@@ -1,4 +1,6 @@
.link-card { .link-card {
overflow: hidden;
.row > div { .row > div {
max-height: 400px; max-height: 400px;
overflow: hidden; overflow: hidden;
+1
View File
@@ -77,3 +77,4 @@
#navbarAccountDropdownSp { #navbarAccountDropdownSp {
max-width: calc(100vw - 5em); max-width: calc(100vw - 5em);
} }
@@ -2,7 +2,7 @@
<a class="text-dark card-link" href="{{ $link }}" target="_blank" rel="noopener"> <a class="text-dark card-link" href="{{ $link }}" target="_blank" rel="noopener">
<div class="row no-gutters"> <div class="row no-gutters">
<div class="col-12 col-md-6 justify-content-center align-items-center"> <div class="col-12 col-md-6 justify-content-center align-items-center">
<img src="" alt="Thumbnail" class="card-img-top-to-left bg-secondary"> <img src="" alt="Thumbnail" class="w-100 bg-secondary">
</div> </div>
<div class="col-12 col-md-6"> <div class="col-12 col-md-6">
<div class="card-body"> <div class="card-body">
+8 -1
View File
@@ -15,7 +15,14 @@
<h5 class="my-4">Shikontribution graph</h5> <h5 class="my-4">Shikontribution graph</h5>
<div id="cal-heatmap" class="tis-contribution-graph"></div> <div id="cal-heatmap" class="tis-contribution-graph"></div>
<hr class="my-4"> <hr class="my-4">
<h5 class="my-4">月間チェックイン回数</h5> <div class="row my-4">
<div class="col-12 col-lg-6 d-flex align-items-center">
<h5 class="my-0">月間チェックイン回数</h5>
</div>
<div class="col-12 col-lg-6 mt-2 mt-lg-0">
<select id="monthly-term" class="form-control"></select>
</div>
</div>
<canvas id="monthly-graph" class="w-100"></canvas> <canvas id="monthly-graph" class="w-100"></canvas>
<hr class="my-4"> <hr class="my-4">
<h5 class="my-4">年間チェックイン回数</h5> <h5 class="my-4">年間チェックイン回数</h5>
@@ -22,12 +22,12 @@ trait CreateMockedResolver
/** /**
* @param string $resolverClass * @param string $resolverClass
* @param string $responseText * @param string|array $response
* @param array $headers * @param array $headers
* @param int $status * @param int $status
* @return Resolver * @return Resolver
*/ */
protected function createResolver(string $resolverClass, string $responseText, array $headers = [], int $status = 200) protected function createResolver(string $resolverClass, $response, array $headers = [], int $status = 200)
{ {
if (!$this->shouldUseMock()) { if (!$this->shouldUseMock()) {
$this->resolver = app()->make($resolverClass); $this->resolver = app()->make($resolverClass);
@@ -39,8 +39,26 @@ trait CreateMockedResolver
'content-type' => 'text/html', 'content-type' => 'text/html',
]; ];
$mockResponse = new Response($status, $headers, $responseText); if (is_array($response)) {
$responses = $response;
$mockResponses = [];
foreach ($responses as $response) {
if (is_array($response)) {
$response += [
1 => $headers,
2 => $status
];
array_push($mockResponses, new Response($response[2], $response[1], $response[0]));
} else {
array_push($mockResponses, new Response($status, $headers, $response));
}
}
$this->handler = new MockHandler($mockResponses);
} else {
$mockResponse = new Response($status, $headers, $response);
$this->handler = new MockHandler([$mockResponse]); $this->handler = new MockHandler([$mockResponse]);
}
$client = new Client(['handler' => $this->handler]); $client = new Client(['handler' => $this->handler]);
$this->resolver = app()->make($resolverClass, ['client' => $client]); $this->resolver = app()->make($resolverClass, ['client' => $client]);
@@ -40,13 +40,13 @@ class PixivResolverTest extends TestCase
$this->createResolver(PixivResolver::class, $responseText); $this->createResolver(PixivResolver::class, $responseText);
$metadata = $this->resolver->resolve('https://www.pixiv.net/member_illust.php?mode=medium&illust_id=74939802'); $metadata = $this->resolver->resolve('https://www.pixiv.net/member_illust.php?mode=medium&illust_id=75899985');
$this->assertEquals('T-20S', $metadata->title); $this->assertEquals('コミッション絵33', $metadata->title);
$this->assertEquals('投稿者: amssc' . PHP_EOL . 'JUST FOR FUN' . PHP_EOL . '现在可以做到游戏内立绘修改拉!立绘动态皮肤都可以支持,想要资助获得新技术请站内信联系我。', $metadata->description); $this->assertEquals('投稿者: ナゼ(NAZE)' . PHP_EOL . 'Leak' . PHP_EOL . PHP_EOL . 'Character:アリッサさん(依頼主のオリキャラ)', $metadata->description);
$this->assertEquals('https://i.pixiv.cat/img-master/img/2019/05/28/01/16/24/74939802_p0_master1200.jpg', $metadata->image); $this->assertEquals('https://i.pixiv.cat/img-master/img/2019/07/25/13/02/59/75899985_p0_master1200.jpg', $metadata->image);
$this->assertEquals(['巨乳', '乳', 'lastorigin', 'Last_Origin', 'T-20S', 'おっぱい', '라스트오리진', '노움'], $metadata->tags); $this->assertEquals(['巨乳', '乳', '巨乳首', '母乳'], $metadata->tags);
if ($this->shouldUseMock()) { if ($this->shouldUseMock()) {
$this->assertSame('https://www.pixiv.net/ajax/illust/74939802', (string) $this->handler->getLastRequest()->getUri()); $this->assertSame('https://www.pixiv.net/ajax/illust/75899985', (string) $this->handler->getLastRequest()->getUri());
} }
} }
@@ -0,0 +1,140 @@
<?php
namespace Tests\Unit\MetadataResolver;
use App\MetadataResolver\ToranoanaResolver;
use Tests\TestCase;
class ToranoanaResolverTest extends TestCase
{
use CreateMockedResolver;
public function setUp()
{
parent::setUp();
if (!$this->shouldUseMock()) {
sleep(1);
}
}
public function testTora()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Toranoana/testTora.html');
$this->createResolver(ToranoanaResolver::class, $responseText);
$metadata = $this->resolver->resolve('https://ec.toranoana.shop/tora/ec/item/040030720152');
$this->assertEquals('新・古明地喫茶~そしてまた扉は開く~', $metadata->title);
$this->assertEquals('サークル【ツキギのとこ】(槻木こうすけ)発行の「新・古明地喫茶~そしてまた扉は開く~」を買うなら、とらのあな全年齢向け通信販売!', $metadata->description);
$this->assertRegExp('~ecdnimg\.toranoana\.jp/ec/img/.*\.jpg~', $metadata->image);
if ($this->shouldUseMock()) {
$this->assertSame('https://ec.toranoana.shop/tora/ec/item/040030720152', (string) $this->handler->getLastRequest()->getUri());
}
}
public function testToraR()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Toranoana/testToraR.html');
$this->createResolver(ToranoanaResolver::class, $responseText);
$metadata = $this->resolver->resolve('https://ec.toranoana.jp/tora_r/ec/item/040030720174');
$this->assertEquals('お姉ちゃんが妹のぱんつでひとりえっちしてました。', $metadata->title);
$this->assertEquals('サークル【没後】(RYO)発行の「お姉ちゃんが妹のぱんつでひとりえっちしてました。」を買うなら、とらのあな成年向け通信販売!', $metadata->description);
$this->assertRegExp('~ecdnimg\.toranoana\.jp/ec/img/.*\.jpg~', $metadata->image);
if ($this->shouldUseMock()) {
$this->assertSame('https://ec.toranoana.jp/tora_r/ec/item/040030720174', (string) $this->handler->getLastRequest()->getUri());
}
}
public function testToraD()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Toranoana/testToraD.html');
$this->createResolver(ToranoanaResolver::class, $responseText);
$metadata = $this->resolver->resolve('https://ec.toranoana.shop/tora_d/digi/item/042000013358');
$this->assertEquals('虎の穴ラボの薄い本。vol 1.5', $metadata->title);
$this->assertEquals('サークル【虎の穴ラボ】(虎の穴ラボエンジニアチーム)発行の「虎の穴ラボの薄い本。vol 1.5」を買うなら、とらのあな全年齢向け電子書籍!', $metadata->description);
$this->assertRegExp('~ecdnimg\.toranoana\.jp/ec/img/.*\.jpg~', $metadata->image);
if ($this->shouldUseMock()) {
$this->assertSame('https://ec.toranoana.shop/tora_d/digi/item/042000013358', (string) $this->handler->getLastRequest()->getUri());
}
}
public function testToraRD()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Toranoana/testToraRD.html');
$this->createResolver(ToranoanaResolver::class, $responseText);
$metadata = $this->resolver->resolve('https://ec.toranoana.jp/tora_rd/digi/item/042000013181');
$this->assertEquals('放課後のお花摘み', $metadata->title);
$this->assertEquals('サークル【給食泥棒】(村雲)発行の「放課後のお花摘み」を買うなら、とらのあな成年向け電子書籍!', $metadata->description);
$this->assertRegExp('~ecdnimg\.toranoana\.jp/ec/img/.*\.jpg~', $metadata->image);
if ($this->shouldUseMock()) {
$this->assertSame('https://ec.toranoana.jp/tora_rd/digi/item/042000013181', (string) $this->handler->getLastRequest()->getUri());
}
}
public function testJoshi()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Toranoana/testJoshi.html');
$this->createResolver(ToranoanaResolver::class, $responseText);
$metadata = $this->resolver->resolve('https://ec.toranoana.shop/joshi/ec/item/040030702729');
$this->assertEquals('円卓のクソ漫画', $metadata->title);
$this->assertEquals('サークル【地獄のすなぎもカーニバル】(槌田)発行の「円卓のクソ漫画」を買うなら、とらのあなJOSHIBU全年齢向け通信販売!', $metadata->description);
$this->assertRegExp('~ecdnimg\.toranoana\.jp/ec/img/.*\.jpg~', $metadata->image);
if ($this->shouldUseMock()) {
$this->assertSame('https://ec.toranoana.shop/joshi/ec/item/040030702729', (string) $this->handler->getLastRequest()->getUri());
}
}
public function testJoshiR()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Toranoana/testJoshiR.html');
$this->createResolver(ToranoanaResolver::class, $responseText);
$metadata = $this->resolver->resolve('https://ec.toranoana.jp/joshi_r/ec/item/040030730126');
$this->assertEquals('リバースナイトリバース', $metadata->title);
$this->assertEquals('サークル【雨傘サイクル】(チャリリズム)発行の「リバースナイトリバース」を買うなら、とらのあなJOSHIBU成年向け通信販売!', $metadata->description);
$this->assertRegExp('~ecdnimg\.toranoana\.jp/ec/img/.*\.jpg~', $metadata->image);
if ($this->shouldUseMock()) {
$this->assertSame('https://ec.toranoana.jp/joshi_r/ec/item/040030730126', (string) $this->handler->getLastRequest()->getUri());
}
}
public function testJoshiD()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Toranoana/testJoshiD.html');
$this->createResolver(ToranoanaResolver::class, $responseText);
$metadata = $this->resolver->resolve('https://ec.toranoana.shop/joshi_d/digi/item/042000012192');
$this->assertEquals('超幸運ガール審神者GOLDEN', $metadata->title);
$this->assertEquals('サークル【Day Of The Dead】(ほんちゅ)発行の「超幸運ガール審神者GOLDEN」を買うなら、とらのあなJOSHIBU全年齢向け電子書籍!', $metadata->description);
$this->assertRegExp('~ecdnimg\.toranoana\.jp/ec/img/.*\.jpg~', $metadata->image);
if ($this->shouldUseMock()) {
$this->assertSame('https://ec.toranoana.shop/joshi_d/digi/item/042000012192', (string) $this->handler->getLastRequest()->getUri());
}
}
public function testJoshiRD()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Toranoana/testJoshiRD.html');
$this->createResolver(ToranoanaResolver::class, $responseText);
$metadata = $this->resolver->resolve('https://ec.toranoana.jp/joshi_rd/digi/item/042000013472');
$this->assertEquals('UBWの裏側で非公式に遠坂凛をナデナデする本', $metadata->title);
$this->assertEquals('サークル【阿仁谷組】(阿仁谷ユイジ)発行の「UBWの裏側で非公式に遠坂凛をナデナデする本」を買うなら、とらのあなJOSHIBU成年向け電子書籍!', $metadata->description);
$this->assertRegExp('~ecdnimg\.toranoana\.jp/ec/img/.*\.jpg~', $metadata->image);
if ($this->shouldUseMock()) {
$this->assertSame('https://ec.toranoana.jp/joshi_rd/digi/item/042000013472', (string) $this->handler->getLastRequest()->getUri());
}
}
}
@@ -0,0 +1,51 @@
<?php
namespace Tests\Unit\MetadataResolver;
use App\MetadataResolver\XtubeResolver;
use Tests\TestCase;
class XtubeResolverTest extends TestCase
{
use CreateMockedResolver;
public function setUp()
{
parent::setUp();
if (!$this->shouldUseMock()) {
sleep(1);
}
}
public function test()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Xtube/test.json');
$this->createResolver(XtubeResolver::class, $responseText);
$metadata = $this->resolver->resolve('https://www.xtube.com/video-watch/homegrown-big-tits-18634762');
$this->assertEquals('Homegrown Big Tits', $metadata->title);
$this->assertEquals('Dedicated to the fans of the beautiful amateur women with big natural tits. All user submitted - you can see big boob amateur hotties fucking and sucking as their tits bounce and sway.', $metadata->description);
$this->assertRegExp('~https://cdn\d-s-hw-e5\.xtube\.com/m=eSK08f/videos/201302/07/RF4Nk-S774-/240X180/1\.jpg~', $metadata->image);
$this->assertEquals(['bigtits', 'homeg'], $metadata->tags);
}
public function testNotMatch()
{
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('Unmatched URL Pattern: https://www.xtube.com/gallery/black-celebs-free-7686657');
$this->createResolver(XtubeResolver::class, '');
$this->resolver->resolve('https://www.xtube.com/gallery/black-celebs-free-7686657');
}
public function testNotOK()
{
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('404: https://www.xtube.com/video-watch/notfound-404');
$this->createResolver(XtubeResolver::class, '', [], 404);
$this->resolver->resolve('https://www.xtube.com/video-watch/notfound-404');
}
}
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
{"duration":"180","views":3146,"video_id":"RF4Nk-S774-","rating":"4.000","ratings":"1","title":"Homegrown Big Tits","description":"Dedicated to the fans of the beautiful amateur women with big natural tits. All user submitted - you can see big boob amateur hotties fucking and sucking as their tits bounce and sway.","url":"https:\/\/www.xtube.com\/video-watch\/homegrown-big-tits-18634762","embedCode":"https:\/\/www.xtube.com\/video-watch\/embedded\/homegrown-big-tits-18634762","default_thumb":"https:\/\/cdn5-s-hw-e5.xtube.com\/m=eSuQ8f\/videos\/201302\/07\/RF4Nk-S774-\/240X180\/1.jpg","thumb":"https:\/\/cdn5-s-hw-e5.xtube.com\/m=eSuQ8f\/videos\/201302\/07\/RF4Nk-S774-\/240X180\/1.jpg","publish_date":"2013-02-07 17:41:10","tags":{"1396":"bigtits","472012":"homeg"},"thumbs":[{"width":300,"height":210,"src":"https:\/\/cdn4-s-hw-e5.xtube.com\/m=eSK08f\/videos\/201302\/07\/RF4Nk-S774-\/240X180\/1.jpg"},{"width":300,"height":210,"src":"https:\/\/cdn4-s-hw-e5.xtube.com\/m=eSK08f\/videos\/201302\/07\/RF4Nk-S774-\/240X180\/2.jpg"},{"width":300,"height":210,"src":"https:\/\/cdn10-s-hw-e5.xtube.com\/m=eSK08f\/videos\/201302\/07\/RF4Nk-S774-\/240X180\/3.jpg"}]}
+4 -4
View File
@@ -2333,7 +2333,7 @@ d3@^3.0.6:
resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.17.tgz#bc46748004378b21a360c9fc7cf5231790762fb8" resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.17.tgz#bc46748004378b21a360c9fc7cf5231790762fb8"
integrity sha1-vEZ0gAQ3iyGjYMn8fPUjF5B2L7g= integrity sha1-vEZ0gAQ3iyGjYMn8fPUjF5B2L7g=
date-fns@^1.27.2: date-fns@^1.27.2, date-fns@^1.30.1:
version "1.30.1" version "1.30.1"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c"
integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==
@@ -4607,9 +4607,9 @@ lodash.uniq@^4.5.0:
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5: lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5:
version "4.17.11" version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
log-symbols@^1.0.2: log-symbols@^1.0.2:
version "1.0.2" version "1.0.2"