commit
0c18965ade
@ -86,6 +86,15 @@ docker-compose run --rm web yarn watch
|
|||||||
現在Docker環境でのHMRはサポートしてません。Docker外ならおそらく動くでしょう。
|
現在Docker環境でのHMRはサポートしてません。Docker外ならおそらく動くでしょう。
|
||||||
その他詳しくはlaravel-mixのドキュメントなどを当たってください。
|
その他詳しくはlaravel-mixのドキュメントなどを当たってください。
|
||||||
|
|
||||||
|
## phpunit によるテスト
|
||||||
|
|
||||||
|
変更をしたらPull Requestを投げる前にテストが通ることを確認してください。
|
||||||
|
テストは以下のコマンドで実行できます。
|
||||||
|
|
||||||
|
```
|
||||||
|
docker-compose exec web composer test
|
||||||
|
```
|
||||||
|
|
||||||
## 環境構築上の諸注意
|
## 環境構築上の諸注意
|
||||||
|
|
||||||
- 初版時点では、DB サーバとして PostgreSQL を使うよう .env ファイルを設定するくらいです。
|
- 初版時点では、DB サーバとして PostgreSQL を使うよう .env ファイルを設定するくらいです。
|
||||||
|
@ -2,11 +2,15 @@
|
|||||||
|
|
||||||
namespace App;
|
namespace App;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Staudenmeir\EloquentEagerLimit\HasEagerLimit;
|
||||||
|
|
||||||
class Ejaculation extends Model
|
class Ejaculation extends Model
|
||||||
{
|
{
|
||||||
//
|
use HasEagerLimit;
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'user_id', 'ejaculated_date',
|
'user_id', 'ejaculated_date',
|
||||||
@ -34,4 +38,45 @@ class Ejaculation extends Model
|
|||||||
return $v->name;
|
return $v->name;
|
||||||
})->all());
|
})->all());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function likes()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Like::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function scopeWithLikes(Builder $query)
|
||||||
|
{
|
||||||
|
if (Auth::check()) {
|
||||||
|
// TODO - このスコープを使うことでlikesが常に直近10件で絞られるのは汚染されすぎ感がある。別名を付与できないか?
|
||||||
|
// - (ejaculation_id, user_id) でユニークなわけですが、is_liked はサブクエリ発行させるのとLeft JoinしてNULLかどうかで結果を見るのどっちがいいんでしょうね
|
||||||
|
return $query
|
||||||
|
->with([
|
||||||
|
'likes' => function ($query) {
|
||||||
|
$query->latest()->take(10);
|
||||||
|
},
|
||||||
|
'likes.user' => function ($query) {
|
||||||
|
$query->where('is_protected', false)
|
||||||
|
->orWhere('id', Auth::id());
|
||||||
|
}
|
||||||
|
])
|
||||||
|
->withCount([
|
||||||
|
'likes',
|
||||||
|
'likes as is_liked' => function ($query) {
|
||||||
|
$query->where('user_id', Auth::id());
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
return $query
|
||||||
|
->with([
|
||||||
|
'likes' => function ($query) {
|
||||||
|
$query->latest()->take(10);
|
||||||
|
},
|
||||||
|
'likes.user' => function ($query) {
|
||||||
|
$query->where('is_protected', false);
|
||||||
|
}
|
||||||
|
])
|
||||||
|
->withCount('likes')
|
||||||
|
->addSelect(DB::raw('0 as is_liked'));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
73
app/Http/Controllers/Api/LikeController.php
Normal file
73
app/Http/Controllers/Api/LikeController.php
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\Ejaculation;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Like;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
|
||||||
|
class LikeController extends Controller
|
||||||
|
{
|
||||||
|
public function store(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'id' => 'required|integer|exists:ejaculations'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$keys = [
|
||||||
|
'user_id' => Auth::id(),
|
||||||
|
'ejaculation_id' => $request->input('id')
|
||||||
|
];
|
||||||
|
|
||||||
|
$like = Like::query()->where($keys)->first();
|
||||||
|
if ($like) {
|
||||||
|
$data = [
|
||||||
|
'errors' => [
|
||||||
|
['message' => 'このチェックインはすでにいいね済です。']
|
||||||
|
],
|
||||||
|
'ejaculation' => $like->ejaculation
|
||||||
|
];
|
||||||
|
|
||||||
|
return response()->json($data, 409);
|
||||||
|
}
|
||||||
|
|
||||||
|
$like = Like::create($keys);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'ejaculation' => $like->ejaculation
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy($id)
|
||||||
|
{
|
||||||
|
Validator::make(compact('id'), [
|
||||||
|
'id' => 'required|integer'
|
||||||
|
])->validate();
|
||||||
|
|
||||||
|
$like = Like::query()->where([
|
||||||
|
'user_id' => Auth::id(),
|
||||||
|
'ejaculation_id' => $id
|
||||||
|
])->first();
|
||||||
|
if ($like === null) {
|
||||||
|
$ejaculation = Ejaculation::find($id);
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'errors' => [
|
||||||
|
['message' => 'このチェックインはいいねされていません。']
|
||||||
|
],
|
||||||
|
'ejaculation' => $ejaculation
|
||||||
|
];
|
||||||
|
|
||||||
|
return response()->json($data, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
$like->delete();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'ejaculation' => $like->ejaculation
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -78,7 +78,9 @@ class EjaculationController extends Controller
|
|||||||
|
|
||||||
public function show($id)
|
public function show($id)
|
||||||
{
|
{
|
||||||
$ejaculation = Ejaculation::findOrFail($id);
|
$ejaculation = Ejaculation::where('id', $id)
|
||||||
|
->withLikes()
|
||||||
|
->firstOrFail();
|
||||||
$user = User::findOrFail($ejaculation->user_id);
|
$user = User::findOrFail($ejaculation->user_id);
|
||||||
|
|
||||||
// 1つ前のチェックインからの経過時間を求める
|
// 1つ前のチェックインからの経過時間を求める
|
||||||
|
@ -69,6 +69,7 @@ SQL
|
|||||||
->orderBy('ejaculations.ejaculated_date', 'desc')
|
->orderBy('ejaculations.ejaculated_date', 'desc')
|
||||||
->select('ejaculations.*')
|
->select('ejaculations.*')
|
||||||
->with('user', 'tags')
|
->with('user', 'tags')
|
||||||
|
->withLikes()
|
||||||
->take(10)
|
->take(10)
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ class SearchController extends Controller
|
|||||||
->where('is_private', false)
|
->where('is_private', false)
|
||||||
->orderBy('ejaculated_date', 'desc')
|
->orderBy('ejaculated_date', 'desc')
|
||||||
->with(['user', 'tags'])
|
->with(['user', 'tags'])
|
||||||
|
->withLikes()
|
||||||
->paginate(20)
|
->paginate(20)
|
||||||
->appends($inputs);
|
->appends($inputs);
|
||||||
|
|
||||||
|
@ -46,11 +46,12 @@ class SettingController extends Controller
|
|||||||
|
|
||||||
public function updatePrivacy(Request $request)
|
public function updatePrivacy(Request $request)
|
||||||
{
|
{
|
||||||
$inputs = $request->all(['is_protected', 'accept_analytics']);
|
$inputs = $request->all(['is_protected', 'accept_analytics', 'private_likes']);
|
||||||
|
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
$user->is_protected = $inputs['is_protected'] ?? false;
|
$user->is_protected = $inputs['is_protected'] ?? false;
|
||||||
$user->accept_analytics = $inputs['accept_analytics'] ?? false;
|
$user->accept_analytics = $inputs['accept_analytics'] ?? false;
|
||||||
|
$user->private_likes = $inputs['private_likes'] ?? false;
|
||||||
$user->save();
|
$user->save();
|
||||||
|
|
||||||
return redirect()->route('setting.privacy')->with('status', 'プライバシー設定を更新しました。');
|
return redirect()->route('setting.privacy')->with('status', 'プライバシー設定を更新しました。');
|
||||||
|
@ -16,6 +16,7 @@ class TimelineController extends Controller
|
|||||||
->orderBy('ejaculations.ejaculated_date', 'desc')
|
->orderBy('ejaculations.ejaculated_date', 'desc')
|
||||||
->select('ejaculations.*')
|
->select('ejaculations.*')
|
||||||
->with('user', 'tags')
|
->with('user', 'tags')
|
||||||
|
->withLikes()
|
||||||
->paginate(21);
|
->paginate(21);
|
||||||
|
|
||||||
return view('timeline.public')->with(compact('ejaculations'));
|
return view('timeline.public')->with(compact('ejaculations'));
|
||||||
|
@ -41,6 +41,7 @@ SQL
|
|||||||
}
|
}
|
||||||
$ejaculations = $query->orderBy('ejaculated_date', 'desc')
|
$ejaculations = $query->orderBy('ejaculated_date', 'desc')
|
||||||
->with('tags')
|
->with('tags')
|
||||||
|
->withLikes()
|
||||||
->paginate(20);
|
->paginate(20);
|
||||||
|
|
||||||
// よく使っているタグ
|
// よく使っているタグ
|
||||||
@ -176,4 +177,19 @@ SQL
|
|||||||
|
|
||||||
return view('user.profile')->with(compact('user', 'ejaculations'));
|
return view('user.profile')->with(compact('user', 'ejaculations'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function likes($name)
|
||||||
|
{
|
||||||
|
$user = User::where('name', $name)->first();
|
||||||
|
if (empty($user)) {
|
||||||
|
abort(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
$likes = $user->likes()
|
||||||
|
->orderBy('created_at', 'desc')
|
||||||
|
->with('ejaculation.user', 'ejaculation.tags')
|
||||||
|
->paginate(20);
|
||||||
|
|
||||||
|
return view('user.likes')->with(compact('user', 'likes'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,12 @@ class Kernel extends HttpKernel
|
|||||||
\App\Http\Middleware\NormalizeLineEnding::class,
|
\App\Http\Middleware\NormalizeLineEnding::class,
|
||||||
],
|
],
|
||||||
|
|
||||||
|
// 現時点では内部APIしかないので、認証の手間を省くためにステートフルにしている。
|
||||||
'api' => [
|
'api' => [
|
||||||
|
\App\Http\Middleware\EncryptCookies::class,
|
||||||
|
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||||||
|
\Illuminate\Session\Middleware\StartSession::class,
|
||||||
|
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||||
'throttle:60,1',
|
'throttle:60,1',
|
||||||
'bindings',
|
'bindings',
|
||||||
],
|
],
|
||||||
|
23
app/Like.php
Normal file
23
app/Like.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Staudenmeir\EloquentEagerLimit\HasEagerLimit;
|
||||||
|
|
||||||
|
class Like extends Model
|
||||||
|
{
|
||||||
|
use HasEagerLimit;
|
||||||
|
|
||||||
|
protected $fillable = ['user_id', 'ejaculation_id'];
|
||||||
|
|
||||||
|
public function user()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ejaculation()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Ejaculation::class)->withLikes();
|
||||||
|
}
|
||||||
|
}
|
@ -73,6 +73,10 @@ class ActivityPubResolver implements Resolver, Parser
|
|||||||
|
|
||||||
private function html2text(string $html): string
|
private function html2text(string $html): string
|
||||||
{
|
{
|
||||||
|
if (empty($html)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
$html = mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8');
|
$html = mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8');
|
||||||
$html = preg_replace('~<br\s*/?\s*>|</p>\s*<p[^>]*>~i', "\n", $html);
|
$html = preg_replace('~<br\s*/?\s*>|</p>\s*<p[^>]*>~i', "\n", $html);
|
||||||
$dom = new \DOMDocument();
|
$dom = new \DOMDocument();
|
||||||
|
@ -13,7 +13,7 @@ class MetadataResolver implements Resolver
|
|||||||
'~nijie\.info/view(_popup)?\.php~' => NijieResolver::class,
|
'~nijie\.info/view(_popup)?\.php~' => NijieResolver::class,
|
||||||
'~komiflo\.com(/#!)?/comics/(\\d+)~' => KomifloResolver::class,
|
'~komiflo\.com(/#!)?/comics/(\\d+)~' => KomifloResolver::class,
|
||||||
'~www\.melonbooks\.co\.jp/detail/detail\.php~' => MelonbooksResolver::class,
|
'~www\.melonbooks\.co\.jp/detail/detail\.php~' => MelonbooksResolver::class,
|
||||||
'~ec\.toranoana\.jp/tora_r/ec/item/.*~' => ToranoanaResolver::class,
|
'~ec\.toranoana\.(jp|shop)/(tora|joshi)(_[rd]+)?/(ec|digi)/item/~' => ToranoanaResolver::class,
|
||||||
'~iwara\.tv/videos/.*~' => IwaraResolver::class,
|
'~iwara\.tv/videos/.*~' => IwaraResolver::class,
|
||||||
'~www\.dlsite\.com/.*/work/=/product_id/..\d+\.html~' => DLsiteResolver::class,
|
'~www\.dlsite\.com/.*/work/=/product_id/..\d+\.html~' => DLsiteResolver::class,
|
||||||
'~dlsite\.jp/mawtw/..\d+~' => DLsiteResolver::class,
|
'~dlsite\.jp/mawtw/..\d+~' => DLsiteResolver::class,
|
||||||
|
@ -20,6 +20,7 @@ class User extends Authenticatable
|
|||||||
'is_protected', 'accept_analytics',
|
'is_protected', 'accept_analytics',
|
||||||
'display_name', 'description',
|
'display_name', 'description',
|
||||||
'twitter_id', 'twitter_name',
|
'twitter_id', 'twitter_name',
|
||||||
|
'private_likes',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,4 +52,9 @@ class User extends Authenticatable
|
|||||||
{
|
{
|
||||||
return Auth::check() && $this->id === Auth::user()->id;
|
return Auth::check() && $this->id === Auth::user()->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function likes()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Like::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,8 @@
|
|||||||
"guzzlehttp/guzzle": "^6.3",
|
"guzzlehttp/guzzle": "^6.3",
|
||||||
"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"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"barryvdh/laravel-debugbar": "^3.1",
|
"barryvdh/laravel-debugbar": "^3.1",
|
||||||
@ -50,6 +51,9 @@
|
|||||||
],
|
],
|
||||||
"fix": [
|
"fix": [
|
||||||
"php-cs-fixer fix"
|
"php-cs-fixer fix"
|
||||||
|
],
|
||||||
|
"test": [
|
||||||
|
"phpunit"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
|
44
composer.lock
generated
44
composer.lock
generated
@ -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": "79423bebbfa31e28aab2d06ba7e19828",
|
"content-hash": "665f6f5eb180a1295fb60303d2ea5051",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "anhskohbo/no-captcha",
|
"name": "anhskohbo/no-captcha",
|
||||||
@ -1873,6 +1873,48 @@
|
|||||||
],
|
],
|
||||||
"time": "2018-07-19T23:38:55+00:00"
|
"time": "2018-07-19T23:38:55+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "staudenmeir/eloquent-eager-limit",
|
||||||
|
"version": "v1.2",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/staudenmeir/eloquent-eager-limit.git",
|
||||||
|
"reference": "4ee5c70268b5bc019618c7de66145d7addb3c5dc"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/staudenmeir/eloquent-eager-limit/zipball/4ee5c70268b5bc019618c7de66145d7addb3c5dc",
|
||||||
|
"reference": "4ee5c70268b5bc019618c7de66145d7addb3c5dc",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"illuminate/database": "~5.5.43|~5.6.34|5.7.*",
|
||||||
|
"illuminate/support": "^5.5.14",
|
||||||
|
"php": ">=7.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"laravel/homestead": "^7.18",
|
||||||
|
"phpunit/phpunit": "~6.5"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Staudenmeir\\EloquentEagerLimit\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Jonas Staudenmeir",
|
||||||
|
"email": "mail@jonas-staudenmeir.de"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Laravel Eloquent eager loading with limit",
|
||||||
|
"time": "2019-01-23T19:25:27+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "swiftmailer/swiftmailer",
|
"name": "swiftmailer/swiftmailer",
|
||||||
"version": "v6.1.3",
|
"version": "v6.1.3",
|
||||||
|
37
database/migrations/2019_03_26_224641_create_likes_table.php
Normal file
37
database/migrations/2019_03_26_224641_create_likes_table.php
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class CreateLikesTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('likes', function (Blueprint $table) {
|
||||||
|
$table->increments('id');
|
||||||
|
$table->integer('user_id')->index();
|
||||||
|
$table->integer('ejaculation_id')->index();
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->unique(['user_id', 'ejaculation_id']);
|
||||||
|
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||||
|
$table->foreign('ejaculation_id')->references('id')->on('ejaculations')->onDelete('cascade');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('likes');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddPrivateLikesToUsers extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->boolean('private_likes')->default(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('private_likes');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
65
resources/assets/js/app.js
vendored
65
resources/assets/js/app.js
vendored
@ -27,4 +27,69 @@ $(() => {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
$deleteCheckinModal.modal('show', this);
|
$deleteCheckinModal.modal('show', this);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '[data-href]', function (event) {
|
||||||
|
location.href = $(this).data('href');
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '.like-button', function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const $this = $(this);
|
||||||
|
const targetId = $this.data('id');
|
||||||
|
const isLiked = $this.data('liked');
|
||||||
|
|
||||||
|
if (isLiked) {
|
||||||
|
const callback = (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) {
|
||||||
|
if (xhr.status === 404) {
|
||||||
|
callback(JSON.parse(xhr.responseText));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error(xhr);
|
||||||
|
alert('いいねを解除できませんでした。');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const callback = (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) {
|
||||||
|
if (xhr.status === 409) {
|
||||||
|
callback(JSON.parse(xhr.responseText));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error(xhr);
|
||||||
|
alert('いいねできませんでした。');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
3
resources/assets/sass/app.scss
vendored
3
resources/assets/sass/app.scss
vendored
@ -12,4 +12,5 @@ $primary: #e53fb1;
|
|||||||
@import "tissue.css";
|
@import "tissue.css";
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
@import "components/link-card";
|
@import "components/ejaculation";
|
||||||
|
@import "components/link-card";
|
||||||
|
21
resources/assets/sass/components/_ejaculation.scss
vendored
Normal file
21
resources/assets/sass/components/_ejaculation.scss
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
.ejaculation-actions {
|
||||||
|
& > button:not(:last-child) {
|
||||||
|
margin-right: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.like-button {
|
||||||
|
text-decoration: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.like-count:not(:empty) {
|
||||||
|
padding-left: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.like-users {
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.like-users-tall {
|
||||||
|
height: 36px;
|
||||||
|
}
|
4
resources/assets/sass/tissue.css
vendored
4
resources/assets/sass/tissue.css
vendored
@ -40,10 +40,6 @@
|
|||||||
border-top: none;
|
border-top: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeline-action-item {
|
|
||||||
margin-left: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tis-global-count-graph {
|
.tis-global-count-graph {
|
||||||
height: 90px;
|
height: 90px;
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, .125);
|
border-bottom: 1px solid rgba(0, 0, 0, .125);
|
||||||
|
53
resources/views/components/ejaculation.blade.php
Normal file
53
resources/views/components/ejaculation.blade.php
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<!-- span -->
|
||||||
|
<div>
|
||||||
|
<h5>
|
||||||
|
<a href="{{ route('user.profile', ['id' => $ejaculation->user->name]) }}" class="text-dark"><img src="{{ $ejaculation->user->getProfileImageUrl(30) }}" width="30" height="30" class="rounded d-inline-block align-bottom"> <bdi>{{ $ejaculation->user->display_name }}</bdi></a>
|
||||||
|
<a href="{{ route('checkin.show', ['id' => $ejaculation->id]) }}" class="text-muted"><small>{{ $ejaculation->ejaculated_date->format('Y/m/d H:i') }}</small></a>
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
|
<!-- tags -->
|
||||||
|
@if ($ejaculation->tags->isNotEmpty())
|
||||||
|
<p class="mb-2">
|
||||||
|
@foreach ($ejaculation->tags as $tag)
|
||||||
|
<a class="badge badge-secondary" href="{{ route('search', ['q' => $tag->name]) }}"><span class="oi oi-tag"></span> {{ $tag->name }}</a>
|
||||||
|
@endforeach
|
||||||
|
</p>
|
||||||
|
@endif
|
||||||
|
<!-- okazu link -->
|
||||||
|
@if (!empty($ejaculation->link))
|
||||||
|
<div class="row mx-0">
|
||||||
|
@component('components.link-card', ['link' => $ejaculation->link])
|
||||||
|
@endcomponent
|
||||||
|
<p class="d-flex align-items-baseline mb-2 col-12 px-0">
|
||||||
|
<span class="oi oi-link-intact mr-1"></span><a class="overflow-hidden" href="{{ $ejaculation->link }}" target="_blank" rel="noopener">{{ $ejaculation->link }}</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
<!-- note -->
|
||||||
|
@if (!empty($ejaculation->note))
|
||||||
|
<p class="mb-2 text-break">
|
||||||
|
{!! Formatter::linkify(nl2br(e($ejaculation->note))) !!}
|
||||||
|
</p>
|
||||||
|
@endif
|
||||||
|
<!-- likes -->
|
||||||
|
@if ($ejaculation->likes_count > 0)
|
||||||
|
<div class="my-2 py-1 border-top border-bottom d-flex align-items-center">
|
||||||
|
<div class="ml-2 mr-3 text-secondary flex-shrink-0"><small><strong>{{ $ejaculation->likes_count }}</strong> 件のいいね</small></div>
|
||||||
|
<div class="like-users flex-grow-1 overflow-hidden">
|
||||||
|
@foreach ($ejaculation->likes as $like)
|
||||||
|
@if ($like->user !== null)
|
||||||
|
<a href="{{ route('user.profile', ['name' => $like->user->name]) }}"><img src="{{ $like->user->getProfileImageUrl(30) }}" width="30" height="30" class="rounded" data-toggle="tooltip" data-placement="bottom" title="{{ $like->user->display_name }}"></a>
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
<!-- actions -->
|
||||||
|
<div class="ejaculation-actions">
|
||||||
|
<button type="button" class="btn btn-link text-secondary"
|
||||||
|
data-toggle="tooltip" data-placement="bottom"
|
||||||
|
title="同じオカズでチェックイン" data-href="{{ route('checkin', ['link' => $ejaculation->link, 'tags' => $ejaculation->textTags()]) }}"><span class="oi oi-reload"></span></button>
|
||||||
|
<button type="button" class="btn btn-link text-secondary like-button"
|
||||||
|
data-toggle="tooltip" data-placement="bottom" data-trigger="hover"
|
||||||
|
title="いいね" data-id="{{ $ejaculation->id }}" data-liked="{{ (bool)$ejaculation->is_liked }}"><span class="oi oi-heart {{ $ejaculation->is_liked ? 'text-danger' : '' }}"></span><span class="like-count">{{ $ejaculation->likes_count ? $ejaculation->likes_count : '' }}</span></button>
|
||||||
|
</div>
|
@ -30,15 +30,8 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<!-- span -->
|
<!-- span -->
|
||||||
<div class="d-flex justify-content-between">
|
<div>
|
||||||
<h5>{{ $ejaculatedSpan ?? '精通' }} <small class="text-muted">{{ $ejaculation->before_date }}{{ !empty($ejaculation->before_date) ? ' ~ ' : '' }}{{ $ejaculation->ejaculated_date->format('Y/m/d H:i') }}</small></h5>
|
<h5>{{ $ejaculatedSpan ?? '精通' }} <small class="text-muted">{{ $ejaculation->before_date }}{{ !empty($ejaculation->before_date) ? ' ~ ' : '' }}{{ $ejaculation->ejaculated_date->format('Y/m/d H:i') }}</small></h5>
|
||||||
<div>
|
|
||||||
<a class="text-secondary timeline-action-item" href="{{ route('checkin', ['link' => $ejaculation->link, 'tags' => $ejaculation->textTags()]) }}"><span class="oi oi-reload" data-toggle="tooltip" data-placement="bottom" title="同じオカズでチェックイン"></span></a>
|
|
||||||
@if ($user->isMe())
|
|
||||||
<a class="text-secondary timeline-action-item" href="{{ route('checkin.edit', ['id' => $ejaculation->id]) }}"><span class="oi oi-pencil" data-toggle="tooltip" data-placement="bottom" title="修正"></span></a>
|
|
||||||
<a class="text-secondary timeline-action-item" href="#" data-target="#deleteCheckinModal" data-id="{{ $ejaculation->id }}" data-date="{{ $ejaculation->ejaculated_date }}"><span class="oi oi-trash" data-toggle="tooltip" data-placement="bottom" title="削除"></span></a>
|
|
||||||
@endif
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<!-- tags -->
|
<!-- tags -->
|
||||||
@if ($ejaculation->is_private || $ejaculation->tags->isNotEmpty())
|
@if ($ejaculation->is_private || $ejaculation->tags->isNotEmpty())
|
||||||
@ -63,10 +56,32 @@
|
|||||||
@endif
|
@endif
|
||||||
<!-- note -->
|
<!-- note -->
|
||||||
@if (!empty($ejaculation->note))
|
@if (!empty($ejaculation->note))
|
||||||
<p class="mb-0 text-break">
|
<p class="mb-2 text-break">
|
||||||
{!! Formatter::linkify(nl2br(e($ejaculation->note))) !!}
|
{!! Formatter::linkify(nl2br(e($ejaculation->note))) !!}
|
||||||
</p>
|
</p>
|
||||||
@endif
|
@endif
|
||||||
|
<!-- likes -->
|
||||||
|
@if ($ejaculation->likes_count > 0)
|
||||||
|
<div class="my-2 py-1 border-top border-bottom d-flex align-items-center">
|
||||||
|
<div class="ml-2 mr-3 text-secondary flex-shrink-0"><small><strong>{{ $ejaculation->likes_count }}</strong> 件のいいね</small></div>
|
||||||
|
<div class="like-users-tall flex-grow-1 overflow-hidden">
|
||||||
|
@foreach ($ejaculation->likes as $like)
|
||||||
|
@if ($like->user !== null)
|
||||||
|
<a href="{{ route('user.profile', ['name' => $like->user->name]) }}"><img src="{{ $like->user->getProfileImageUrl(36) }}" width="36" height="36" class="rounded" data-toggle="tooltip" data-placement="bottom" title="{{ $like->user->display_name }}"></a>
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
<!-- actions -->
|
||||||
|
<div class="ejaculation-actions">
|
||||||
|
<button type="button" class="btn btn-link text-secondary" data-toggle="tooltip" data-placement="bottom" title="同じオカズでチェックイン" data-href="{{ route('checkin', ['link' => $ejaculation->link, 'tags' => $ejaculation->textTags()]) }}"><span class="oi oi-reload"></span></button>
|
||||||
|
<button type="button" class="btn btn-link text-secondary like-button" data-toggle="tooltip" data-placement="bottom" data-trigger="hover" title="いいね" data-id="{{ $ejaculation->id }}" data-liked="{{ (bool)$ejaculation->is_liked }}"><span class="oi oi-heart {{ $ejaculation->is_liked ? 'text-danger' : '' }}"></span><span class="like-count">{{ $ejaculation->likes_count ? $ejaculation->likes_count : '' }}</span></button>
|
||||||
|
@if ($user->isMe())
|
||||||
|
<button type="button" class="btn btn-link text-secondary" data-toggle="tooltip" data-placement="bottom" title="修正" data-href="{{ route('checkin.edit', ['id' => $ejaculation->id]) }}"><span class="oi oi-pencil"></span></button>
|
||||||
|
<button type="button" class="btn btn-link text-secondary" data-toggle="tooltip" data-placement="bottom" title="削除" data-target="#deleteCheckinModal" data-id="{{ $ejaculation->id }}" data-date="{{ $ejaculation->ejaculated_date }}"><span class="oi oi-trash"></span></button>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
@ -55,40 +55,8 @@
|
|||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
@foreach ($publicLinkedEjaculations as $ejaculation)
|
@foreach ($publicLinkedEjaculations as $ejaculation)
|
||||||
<li class="list-group-item no-side-border pt-3 pb-3 text-break">
|
<li class="list-group-item no-side-border pt-3 pb-3 text-break">
|
||||||
<!-- span -->
|
@component('components.ejaculation', compact('ejaculation'))
|
||||||
<div class="d-flex justify-content-between">
|
@endcomponent
|
||||||
<h5>
|
|
||||||
<a href="{{ route('user.profile', ['id' => $ejaculation->user->name]) }}" class="text-dark"><img src="{{ $ejaculation->user->getProfileImageUrl(30) }}" width="30" height="30" class="rounded d-inline-block align-bottom"> <bdi>{{ $ejaculation->user->display_name }}</bdi></a>
|
|
||||||
<a href="{{ route('checkin.show', ['id' => $ejaculation->id]) }}" class="text-muted"><small>{{ $ejaculation->ejaculated_date->format('Y/m/d H:i') }}</small></a>
|
|
||||||
</h5>
|
|
||||||
<div>
|
|
||||||
<a class="text-secondary timeline-action-item" href="{{ route('checkin', ['link' => $ejaculation->link, 'tags' => $ejaculation->textTags()]) }}"><span class="oi oi-reload" data-toggle="tooltip" data-placement="bottom" title="同じオカズでチェックイン"></span></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- tags -->
|
|
||||||
@if ($ejaculation->tags->isNotEmpty())
|
|
||||||
<p class="mb-2">
|
|
||||||
@foreach ($ejaculation->tags as $tag)
|
|
||||||
<a class="badge badge-secondary" href="{{ route('search', ['q' => $tag->name]) }}"><span class="oi oi-tag"></span> {{ $tag->name }}</a>
|
|
||||||
@endforeach
|
|
||||||
</p>
|
|
||||||
@endif
|
|
||||||
<!-- okazu link -->
|
|
||||||
@if (!empty($ejaculation->link))
|
|
||||||
<div class="row mx-0">
|
|
||||||
@component('components.link-card', ['link' => $ejaculation->link])
|
|
||||||
@endcomponent
|
|
||||||
<p class="d-flex align-items-baseline mb-2 col-12 px-0">
|
|
||||||
<span class="oi oi-link-intact mr-1"></span><a class="overflow-hidden" href="{{ $ejaculation->link }}" target="_blank" rel="noopener">{{ $ejaculation->link }}</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
<!-- note -->
|
|
||||||
@if (!empty($ejaculation->note))
|
|
||||||
<p class="mb-0 text-break">
|
|
||||||
{!! Formatter::linkify(nl2br(e($ejaculation->note))) !!}
|
|
||||||
</p>
|
|
||||||
@endif
|
|
||||||
</li>
|
</li>
|
||||||
@endforeach
|
@endforeach
|
||||||
<li class="list-group-item no-side-border text-right">
|
<li class="list-group-item no-side-border text-right">
|
||||||
|
@ -50,6 +50,9 @@
|
|||||||
</p>
|
</p>
|
||||||
</a>
|
</a>
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-divider"></div>
|
||||||
|
<a href="{{ route('user.profile', ['name' => Auth::user()->name]) }}" class="dropdown-item">プロフィール</a>
|
||||||
|
<a href="{{ route('user.likes', ['name' => Auth::user()->name]) }}" class="dropdown-item">いいね</a>
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
<a href="{{ route('setting') }}" class="dropdown-item">設定</a>
|
<a href="{{ route('setting') }}" class="dropdown-item">設定</a>
|
||||||
<a href="{{ route('logout') }}" class="dropdown-item" onclick="event.preventDefault(); document.getElementById('logout-form').submit();">ログアウト</a>
|
<a href="{{ route('logout') }}" class="dropdown-item" onclick="event.preventDefault(); document.getElementById('logout-form').submit();">ログアウト</a>
|
||||||
</div>
|
</div>
|
||||||
@ -104,6 +107,9 @@
|
|||||||
</p>
|
</p>
|
||||||
</a>
|
</a>
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-divider"></div>
|
||||||
|
<a href="{{ route('user.profile', ['name' => Auth::user()->name]) }}" class="dropdown-item">プロフィール</a>
|
||||||
|
<a href="{{ route('user.likes', ['name' => Auth::user()->name]) }}" class="dropdown-item">いいね</a>
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
<a href="{{ route('setting') }}" class="dropdown-item">設定</a>
|
<a href="{{ route('setting') }}" class="dropdown-item">設定</a>
|
||||||
@can ('admin')
|
@can ('admin')
|
||||||
<a href="{{ route('admin.dashboard') }}" class="dropdown-item">管理</a>
|
<a href="{{ route('admin.dashboard') }}" class="dropdown-item">管理</a>
|
||||||
|
@ -7,43 +7,8 @@
|
|||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
@foreach($results as $ejaculation)
|
@foreach($results as $ejaculation)
|
||||||
<li class="list-group-item border-bottom-only pt-3 pb-3 text-break">
|
<li class="list-group-item border-bottom-only pt-3 pb-3 text-break">
|
||||||
<!-- span -->
|
@component('components.ejaculation', compact('ejaculation'))
|
||||||
<div class="d-flex justify-content-between">
|
@endcomponent
|
||||||
<h5>
|
|
||||||
<a href="{{ route('user.profile', ['id' => $ejaculation->user->name]) }}" class="text-dark"><img src="{{ $ejaculation->user->getProfileImageUrl(30) }}" width="30" height="30" class="rounded d-inline-block align-bottom"> <bdi>{{ $ejaculation->user->display_name }}</bdi></a>
|
|
||||||
<a href="{{ route('checkin.show', ['id' => $ejaculation->id]) }}" class="text-muted"><small>{{ $ejaculation->ejaculated_date->format('Y/m/d H:i') }}</small></a>
|
|
||||||
</h5>
|
|
||||||
<div>
|
|
||||||
<a class="text-secondary timeline-action-item" href="{{ route('checkin', ['link' => $ejaculation->link, 'tags' => $ejaculation->textTags()]) }}"><span class="oi oi-reload" data-toggle="tooltip" data-placement="bottom" title="同じオカズでチェックイン"></span></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- tags -->
|
|
||||||
@if ($ejaculation->is_private || $ejaculation->tags->isNotEmpty())
|
|
||||||
<p class="mb-2">
|
|
||||||
@if ($ejaculation->is_private)
|
|
||||||
<span class="badge badge-warning"><span class="oi oi-lock-locked"></span> 非公開</span>
|
|
||||||
@endif
|
|
||||||
@foreach ($ejaculation->tags as $tag)
|
|
||||||
<a class="badge badge-secondary" href="{{ route('search', ['q' => $tag->name]) }}"><span class="oi oi-tag"></span> {{ $tag->name }}</a>
|
|
||||||
@endforeach
|
|
||||||
</p>
|
|
||||||
@endif
|
|
||||||
<!-- okazu link -->
|
|
||||||
@if (!empty($ejaculation->link))
|
|
||||||
<div class="row mx-0">
|
|
||||||
@component('components.link-card', ['link' => $ejaculation->link])
|
|
||||||
@endcomponent
|
|
||||||
<p class="d-flex align-items-baseline mb-2 col-12 px-0">
|
|
||||||
<span class="oi oi-link-intact mr-1"></span><a class="overflow-hidden" href="{{ $ejaculation->link }}" target="_blank" rel="noopener">{{ $ejaculation->link }}</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
<!-- note -->
|
|
||||||
@if (!empty($ejaculation->note))
|
|
||||||
<p class="mb-0 text-break">
|
|
||||||
{!! Formatter::linkify(nl2br(e($ejaculation->note))) !!}
|
|
||||||
</p>
|
|
||||||
@endif
|
|
||||||
</li>
|
</li>
|
||||||
@endforeach
|
@endforeach
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -11,6 +11,11 @@
|
|||||||
<div class="custom-control custom-checkbox mb-2">
|
<div class="custom-control custom-checkbox mb-2">
|
||||||
<input id="protected" name="is_protected" class="custom-control-input" type="checkbox" {{ (old('is_protected') ?? Auth::user()->is_protected ) ? 'checked' : '' }}>
|
<input id="protected" name="is_protected" class="custom-control-input" type="checkbox" {{ (old('is_protected') ?? Auth::user()->is_protected ) ? 'checked' : '' }}>
|
||||||
<label class="custom-control-label" for="protected">全てのチェックイン履歴を非公開にする</label>
|
<label class="custom-control-label" for="protected">全てのチェックイン履歴を非公開にする</label>
|
||||||
|
<small class="form-text text-muted">プロフィール情報を除いて、全ての情報が非公開になります。</small>
|
||||||
|
</div>
|
||||||
|
<div class="custom-control custom-checkbox mb-2">
|
||||||
|
<input id="private-likes" name="private_likes" class="custom-control-input" type="checkbox" {{ (old('private_likes') ?? Auth::user()->private_likes ) ? 'checked' : '' }}>
|
||||||
|
<label class="custom-control-label" for="private-likes">いいねしたチェックイン一覧を非公開にする</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="custom-control custom-checkbox">
|
<div class="custom-control custom-checkbox">
|
||||||
<input id="accept-analytics" name="accept_analytics" class="custom-control-input" type="checkbox" {{ (old('accept_analytics') ?? Auth::user()->accept_analytics ) ? 'checked' : '' }}>
|
<input id="accept-analytics" name="accept_analytics" class="custom-control-input" type="checkbox" {{ (old('accept_analytics') ?? Auth::user()->accept_analytics ) ? 'checked' : '' }}>
|
||||||
|
@ -8,6 +8,11 @@
|
|||||||
<form action="{{ route('setting.profile.update') }}" method="post">
|
<form action="{{ route('setting.profile.update') }}" method="post">
|
||||||
{{ csrf_field() }}
|
{{ csrf_field() }}
|
||||||
<div class="from-group">
|
<div class="from-group">
|
||||||
|
<label for="name">アイコン</label>
|
||||||
|
<img src="{{ Auth::user()->getProfileImageUrl(128) }}" class="rounded d-block">
|
||||||
|
<small class="form-text text-muted">変更は<a href="https://gravatar.com/" target="_blank">Gravatar</a>から行えます。</small>
|
||||||
|
</div>
|
||||||
|
<div class="from-group mt-3">
|
||||||
<label for="display_name">名前</label>
|
<label for="display_name">名前</label>
|
||||||
<input id="display_name" name="display_name" type="text" class="form-control {{ $errors->has('display_name') ? ' is-invalid' : '' }}"
|
<input id="display_name" name="display_name" type="text" class="form-control {{ $errors->has('display_name') ? ' is-invalid' : '' }}"
|
||||||
value="{{ old('display_name') ?? Auth::user()->display_name }}" maxlength="20" autocomplete="off">
|
value="{{ old('display_name') ?? Auth::user()->display_name }}" maxlength="20" autocomplete="off">
|
||||||
@ -26,6 +31,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<small class="form-text text-muted">現在は変更できません。</small>
|
<small class="form-text text-muted">現在は変更できません。</small>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="from-group mt-3">
|
||||||
|
<label for="name">メールアドレス</label>
|
||||||
|
<input id="name" name="name" type="text" class="form-control" value="{{ Auth::user()->email }}" disabled>
|
||||||
|
<small class="form-text text-muted">現在は変更できません。</small>
|
||||||
|
</div>
|
||||||
<div class="form-group mt-3">
|
<div class="form-group mt-3">
|
||||||
<label for="bio">自己紹介</label>
|
<label for="bio">自己紹介</label>
|
||||||
<textarea id="bio" name="bio" rows="3" class="form-control {{ $errors->has('bio') ? ' is-invalid' : '' }}">{{ old('bio') ?? Auth::user()->bio }}</textarea>
|
<textarea id="bio" name="bio" rows="3" class="form-control {{ $errors->has('bio') ? ' is-invalid' : '' }}">{{ old('bio') ?? Auth::user()->bio }}</textarea>
|
||||||
|
@ -11,43 +11,8 @@
|
|||||||
<div class="row mx-1">
|
<div class="row mx-1">
|
||||||
@foreach($ejaculations as $ejaculation)
|
@foreach($ejaculations as $ejaculation)
|
||||||
<div class="col-12 col-lg-6 col-xl-4 py-3 text-break border-top">
|
<div class="col-12 col-lg-6 col-xl-4 py-3 text-break border-top">
|
||||||
<!-- span -->
|
@component('components.ejaculation', compact('ejaculation'))
|
||||||
<div class="d-flex justify-content-between">
|
@endcomponent
|
||||||
<h5>
|
|
||||||
<a href="{{ route('user.profile', ['id' => $ejaculation->user->name]) }}" class="text-dark"><img src="{{ $ejaculation->user->getProfileImageUrl(30) }}" width="30" height="30" class="rounded d-inline-block align-bottom"> <bdi>{{ $ejaculation->user->display_name }}</bdi></a>
|
|
||||||
<a href="{{ route('checkin.show', ['id' => $ejaculation->id]) }}" class="text-muted"><small>{{ $ejaculation->ejaculated_date->format('Y/m/d H:i') }}</small></a>
|
|
||||||
</h5>
|
|
||||||
<div>
|
|
||||||
<a class="text-secondary timeline-action-item" href="{{ route('checkin', ['link' => $ejaculation->link, 'tags' => $ejaculation->textTags()]) }}"><span class="oi oi-reload" data-toggle="tooltip" data-placement="bottom" title="同じオカズでチェックイン"></span></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- tags -->
|
|
||||||
@if ($ejaculation->is_private || $ejaculation->tags->isNotEmpty())
|
|
||||||
<p class="mb-2">
|
|
||||||
@if ($ejaculation->is_private)
|
|
||||||
<span class="badge badge-warning"><span class="oi oi-lock-locked"></span> 非公開</span>
|
|
||||||
@endif
|
|
||||||
@foreach ($ejaculation->tags as $tag)
|
|
||||||
<a class="badge badge-secondary" href="{{ route('search', ['q' => $tag->name]) }}"><span class="oi oi-tag"></span> {{ $tag->name }}</a>
|
|
||||||
@endforeach
|
|
||||||
</p>
|
|
||||||
@endif
|
|
||||||
<!-- okazu link -->
|
|
||||||
@if (!empty($ejaculation->link))
|
|
||||||
<div class="row mx-0">
|
|
||||||
@component('components.link-card', ['link' => $ejaculation->link])
|
|
||||||
@endcomponent
|
|
||||||
<p class="d-flex align-items-baseline mb-2 col-12 px-0">
|
|
||||||
<span class="oi oi-link-intact mr-1"></span><a class="overflow-hidden" href="{{ $ejaculation->link }}" target="_blank" rel="noopener">{{ $ejaculation->link }}</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
<!-- note -->
|
|
||||||
@if (!empty($ejaculation->note))
|
|
||||||
<p class="mb-0 text-break">
|
|
||||||
{!! Formatter::linkify(nl2br(e($ejaculation->note))) !!}
|
|
||||||
</p>
|
|
||||||
@endif
|
|
||||||
</div>
|
</div>
|
||||||
@endforeach
|
@endforeach
|
||||||
</div>
|
</div>
|
||||||
|
@ -20,6 +20,13 @@
|
|||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link {{ Route::currentRouteName() === 'user.okazu' ? 'active' : '' }}" href="{{ route('user.okazu', ['name' => $user->name]) }}">オカズ</a>
|
<a class="nav-link {{ Route::currentRouteName() === 'user.okazu' ? 'active' : '' }}" href="{{ route('user.okazu', ['name' => $user->name]) }}">オカズ</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link {{ Route::currentRouteName() === 'user.likes' ? 'active' : '' }}" href="{{ route('user.likes', ['name' => $user->name]) }}">いいね
|
||||||
|
@if ($user->isMe() || !($user->is_protected || $user->private_likes))
|
||||||
|
<span class="badge badge-primary">{{ $user->likes()->count() }}</span>
|
||||||
|
@endif
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
@yield('tab-content')
|
@yield('tab-content')
|
||||||
|
25
resources/views/user/likes.blade.php
Normal file
25
resources/views/user/likes.blade.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
@extends('user.base')
|
||||||
|
|
||||||
|
@section('title', $user->display_name . ' (@' . $user->name . ') さんがいいねしたチェックイン')
|
||||||
|
|
||||||
|
@section('tab-content')
|
||||||
|
@if (($user->is_protected || $user->private_likes) && !$user->isMe())
|
||||||
|
<p class="mt-4">
|
||||||
|
<span class="oi oi-lock-locked"></span> このユーザはいいね一覧を公開していません。
|
||||||
|
</p>
|
||||||
|
@else
|
||||||
|
<ul class="list-group">
|
||||||
|
@forelse ($likes as $like)
|
||||||
|
<li class="list-group-item border-bottom-only pt-3 pb-3 text-break">
|
||||||
|
@component('components.ejaculation', ['ejaculation' => $like->ejaculation])
|
||||||
|
@endcomponent
|
||||||
|
</li>
|
||||||
|
@empty
|
||||||
|
<li class="list-group-item border-bottom-only">
|
||||||
|
<p>まだ何もいいと思ったことがありません。</p>
|
||||||
|
</li>
|
||||||
|
@endforelse
|
||||||
|
</ul>
|
||||||
|
{{ $likes->links(null, ['className' => 'mt-4 justify-content-center']) }}
|
||||||
|
@endif
|
||||||
|
@endsection
|
@ -36,15 +36,8 @@
|
|||||||
@forelse ($ejaculations as $ejaculation)
|
@forelse ($ejaculations as $ejaculation)
|
||||||
<li class="list-group-item border-bottom-only pt-3 pb-3 text-break">
|
<li class="list-group-item border-bottom-only pt-3 pb-3 text-break">
|
||||||
<!-- span -->
|
<!-- span -->
|
||||||
<div class="d-flex justify-content-between">
|
<div>
|
||||||
<h5>{{ $ejaculation->ejaculated_span ?? '精通' }} <a href="{{ route('checkin.show', ['id' => $ejaculation->id]) }}" class="text-muted"><small>{{ $ejaculation->before_date }}{{ !empty($ejaculation->before_date) ? ' ~ ' : '' }}{{ $ejaculation->ejaculated_date->format('Y/m/d H:i') }}</small></a></h5>
|
<h5>{{ $ejaculation->ejaculated_span ?? '精通' }} <a href="{{ route('checkin.show', ['id' => $ejaculation->id]) }}" class="text-muted"><small>{{ $ejaculation->before_date }}{{ !empty($ejaculation->before_date) ? ' ~ ' : '' }}{{ $ejaculation->ejaculated_date->format('Y/m/d H:i') }}</small></a></h5>
|
||||||
<div>
|
|
||||||
<a class="text-secondary timeline-action-item" href="{{ route('checkin', ['link' => $ejaculation->link, 'tags' => $ejaculation->textTags()]) }}"><span class="oi oi-reload" data-toggle="tooltip" data-placement="bottom" title="同じオカズでチェックイン"></span></a>
|
|
||||||
@if ($user->isMe())
|
|
||||||
<a class="text-secondary timeline-action-item" href="{{ route('checkin.edit', ['id' => $ejaculation->id]) }}"><span class="oi oi-pencil" data-toggle="tooltip" data-placement="bottom" title="修正"></span></a>
|
|
||||||
<a class="text-secondary timeline-action-item" href="#" data-target="#deleteCheckinModal" data-id="{{ $ejaculation->id }}" data-date="{{ $ejaculation->ejaculated_date }}"><span class="oi oi-trash" data-toggle="tooltip" data-placement="bottom" title="削除"></span></a>
|
|
||||||
@endif
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<!-- tags -->
|
<!-- tags -->
|
||||||
@if ($ejaculation->is_private || $ejaculation->tags->isNotEmpty())
|
@if ($ejaculation->is_private || $ejaculation->tags->isNotEmpty())
|
||||||
@ -69,10 +62,32 @@
|
|||||||
@endif
|
@endif
|
||||||
<!-- note -->
|
<!-- note -->
|
||||||
@if (!empty($ejaculation->note))
|
@if (!empty($ejaculation->note))
|
||||||
<p class="mb-0 text-break">
|
<p class="mb-2 text-break">
|
||||||
{!! Formatter::linkify(nl2br(e($ejaculation->note))) !!}
|
{!! Formatter::linkify(nl2br(e($ejaculation->note))) !!}
|
||||||
</p>
|
</p>
|
||||||
@endif
|
@endif
|
||||||
|
<!-- likes -->
|
||||||
|
@if ($ejaculation->likes_count > 0)
|
||||||
|
<div class="my-2 py-1 border-top border-bottom d-flex align-items-center">
|
||||||
|
<div class="ml-2 mr-3 text-secondary flex-shrink-0"><small><strong>{{ $ejaculation->likes_count }}</strong> 件のいいね</small></div>
|
||||||
|
<div class="like-users flex-grow-1 overflow-hidden">
|
||||||
|
@foreach ($ejaculation->likes as $like)
|
||||||
|
@if ($like->user !== null)
|
||||||
|
<a href="{{ route('user.profile', ['name' => $like->user->name]) }}"><img src="{{ $like->user->getProfileImageUrl(30) }}" width="30" height="30" class="rounded" data-toggle="tooltip" data-placement="bottom" title="{{ $like->user->display_name }}"></a>
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
<!-- actions -->
|
||||||
|
<div class="ejaculation-actions">
|
||||||
|
<button type="button" class="btn btn-link text-secondary" data-toggle="tooltip" data-placement="bottom" title="同じオカズでチェックイン" data-href="{{ route('checkin', ['link' => $ejaculation->link, 'tags' => $ejaculation->textTags()]) }}"><span class="oi oi-reload"></span></button>
|
||||||
|
<button type="button" class="btn btn-link text-secondary like-button" data-toggle="tooltip" data-placement="bottom" data-trigger="hover" title="いいね" data-id="{{ $ejaculation->id }}" data-liked="{{ (bool)$ejaculation->is_liked }}"><span class="oi oi-heart {{ $ejaculation->is_liked ? 'text-danger' : '' }}"></span><span class="like-count">{{ $ejaculation->likes_count ? $ejaculation->likes_count : '' }}</span></button>
|
||||||
|
@if ($user->isMe())
|
||||||
|
<button type="button" class="btn btn-link text-secondary" data-toggle="tooltip" data-placement="bottom" title="修正" data-href="{{ route('checkin.edit', ['id' => $ejaculation->id]) }}"><span class="oi oi-pencil"></span></button>
|
||||||
|
<button type="button" class="btn btn-link text-secondary" data-toggle="tooltip" data-placement="bottom" title="削除" data-target="#deleteCheckinModal" data-id="{{ $ejaculation->id }}" data-date="{{ $ejaculation->ejaculated_date }}"><span class="oi oi-trash"></span></button>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
</li>
|
</li>
|
||||||
@empty
|
@empty
|
||||||
<li class="list-group-item border-bottom-only">
|
<li class="list-group-item border-bottom-only">
|
||||||
|
@ -16,3 +16,8 @@
|
|||||||
//});
|
//});
|
||||||
|
|
||||||
Route::get('/checkin/card', 'Api\\CardController@show');
|
Route::get('/checkin/card', 'Api\\CardController@show');
|
||||||
|
|
||||||
|
Route::middleware('auth')->group(function () {
|
||||||
|
Route::post('/likes', 'Api\\LikeController@store');
|
||||||
|
Route::delete('/likes/{id}', 'Api\\LikeController@destroy');
|
||||||
|
});
|
||||||
|
@ -19,6 +19,7 @@ Route::get('/user', 'UserController@redirectMypage')->middleware('auth');
|
|||||||
Route::get('/user/{name?}', 'UserController@profile')->name('user.profile');
|
Route::get('/user/{name?}', 'UserController@profile')->name('user.profile');
|
||||||
Route::get('/user/{name}/stats', 'UserController@stats')->name('user.stats');
|
Route::get('/user/{name}/stats', 'UserController@stats')->name('user.stats');
|
||||||
Route::get('/user/{name}/okazu', 'UserController@okazu')->name('user.okazu');
|
Route::get('/user/{name}/okazu', 'UserController@okazu')->name('user.okazu');
|
||||||
|
Route::get('/user/{name}/likes', 'UserController@likes')->name('user.likes');
|
||||||
|
|
||||||
Route::get('/checkin/{id}', 'EjaculationController@show')->name('checkin.show');
|
Route::get('/checkin/{id}', 'EjaculationController@show')->name('checkin.show');
|
||||||
Route::middleware('auth')->group(function () {
|
Route::middleware('auth')->group(function () {
|
||||||
|
Loading…
Reference in New Issue
Block a user