mirror of
https://github.com/pixelfed/pixelfed.git
synced 2024-11-26 16:23:16 +00:00
Merge pull request #1104 from pixelfed/frontend-ui-refactor
Frontend ui refactor
This commit is contained in:
commit
82102376fe
40 changed files with 1439 additions and 3186 deletions
|
@ -9,6 +9,7 @@ use Cache;
|
|||
use App\Comment;
|
||||
use App\Jobs\CommentPipeline\CommentPipeline;
|
||||
use App\Jobs\StatusPipeline\NewStatusPipeline;
|
||||
use App\Util\Lexer\Autolink;
|
||||
use App\Profile;
|
||||
use App\Status;
|
||||
use League\Fractal;
|
||||
|
@ -53,10 +54,11 @@ class CommentController extends Controller
|
|||
|
||||
Cache::forget('transform:status:'.$status->url());
|
||||
|
||||
$autolink = Autolink::create()->autolink($comment);
|
||||
$reply = new Status();
|
||||
$reply->profile_id = $profile->id;
|
||||
$reply->caption = e($comment);
|
||||
$reply->rendered = $comment;
|
||||
$reply->rendered = $autolink;
|
||||
$reply->in_reply_to_id = $status->id;
|
||||
$reply->in_reply_to_profile_id = $status->profile_id;
|
||||
$reply->save();
|
||||
|
|
|
@ -63,6 +63,11 @@ class FederationController extends Controller
|
|||
|
||||
$follower = Auth::user()->profile;
|
||||
$url = $request->input('url');
|
||||
$url = Helpers::validateUrl($url);
|
||||
|
||||
if(!$url) {
|
||||
return;
|
||||
}
|
||||
|
||||
RemoteFollowPipeline::dispatch($follower, $url);
|
||||
|
||||
|
|
|
@ -9,6 +9,11 @@ use App\Status;
|
|||
use Illuminate\Http\Request;
|
||||
use App\Util\ActivityPub\Helpers;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use App\Transformer\Api\{
|
||||
AccountTransformer,
|
||||
HashtagTransformer,
|
||||
StatusTransformer,
|
||||
};
|
||||
|
||||
class SearchController extends Controller
|
||||
{
|
||||
|
@ -22,26 +27,33 @@ class SearchController extends Controller
|
|||
if(mb_strlen($tag) < 3) {
|
||||
return;
|
||||
}
|
||||
$tag = e(urldecode($tag));
|
||||
|
||||
$hash = hash('sha256', $tag);
|
||||
$tokens = Cache::remember('api:search:tag:'.$hash, now()->addMinutes(5), function () use ($tag) {
|
||||
$tokens = collect([]);
|
||||
if(Helpers::validateUrl($tag)) {
|
||||
$tokens = [];
|
||||
if(Helpers::validateUrl($tag) != false) {
|
||||
$remote = Helpers::fetchFromUrl($tag);
|
||||
if(isset($remote['type']) && in_array($remote['type'], ['Create', 'Person']) == true) {
|
||||
$type = $remote['type'];
|
||||
if($type == 'Person') {
|
||||
$item = Helpers::profileFirstOrNew($tag);
|
||||
$tokens->push([[
|
||||
$tokens['profiles'] = [[
|
||||
'count' => 1,
|
||||
'url' => $item->url(),
|
||||
'type' => 'profile',
|
||||
'value' => $item->username,
|
||||
'tokens' => [$item->username],
|
||||
'name' => $item->name,
|
||||
]]);
|
||||
'entity' => [
|
||||
'id' => $item->id,
|
||||
'following' => $item->followedBy(Auth::user()->profile),
|
||||
'thumb' => $item->avatarUrl()
|
||||
]
|
||||
]];
|
||||
} else if ($type == 'Create') {
|
||||
$item = Helpers::statusFirstOrFetch($tag, false);
|
||||
$tokens->push([[
|
||||
$tokens['posts'] = [[
|
||||
'count' => 0,
|
||||
'url' => $item->url(),
|
||||
'type' => 'status',
|
||||
|
@ -49,10 +61,9 @@ class SearchController extends Controller
|
|||
'tokens' => [$item->caption],
|
||||
'name' => $item->caption,
|
||||
'thumb' => $item->thumb(),
|
||||
]]);
|
||||
]];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
$hashtags = Hashtag::select('id', 'name', 'slug')->where('slug', 'like', '%'.$tag.'%')->whereHas('posts')->limit(20)->get();
|
||||
if($hashtags->count() > 0) {
|
||||
|
@ -62,17 +73,19 @@ class SearchController extends Controller
|
|||
'url' => $item->url(),
|
||||
'type' => 'hashtag',
|
||||
'value' => $item->name,
|
||||
'tokens' => explode('-', $item->name),
|
||||
'tokens' => '',
|
||||
'name' => null,
|
||||
];
|
||||
});
|
||||
$tokens->push($tags);
|
||||
$tokens['hashtags'] = $tags;
|
||||
}
|
||||
return $tokens;
|
||||
});
|
||||
$users = Profile::select('username', 'name', 'id')
|
||||
->whereNull('status')
|
||||
->whereNull('domain')
|
||||
->where('id', '!=', Auth::user()->profile->id)
|
||||
->where('username', 'like', '%'.$tag.'%')
|
||||
//->orWhere('remote_url', $tag)
|
||||
->orWhere('remote_url', $tag)
|
||||
->limit(20)
|
||||
->get();
|
||||
|
||||
|
@ -85,14 +98,21 @@ class SearchController extends Controller
|
|||
'value' => $item->username,
|
||||
'tokens' => [$item->username],
|
||||
'name' => $item->name,
|
||||
'id' => $item->id
|
||||
'avatar' => $item->avatarUrl(),
|
||||
'id' => $item->id,
|
||||
'entity' => [
|
||||
'id' => $item->id,
|
||||
'following' => $item->followedBy(Auth::user()->profile),
|
||||
'thumb' => $item->avatarUrl()
|
||||
]
|
||||
];
|
||||
});
|
||||
$tokens->push($profiles);
|
||||
if(isset($tokens['profiles'])) {
|
||||
array_push($tokens['profiles'], $profiles);
|
||||
} else {
|
||||
$tokens['profiles'] = $profiles;
|
||||
}
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
});
|
||||
$posts = Status::select('id', 'profile_id', 'caption', 'created_at')
|
||||
->whereHas('media')
|
||||
->whereNull('in_reply_to_id')
|
||||
|
@ -100,7 +120,8 @@ class SearchController extends Controller
|
|||
->whereProfileId(Auth::user()->profile->id)
|
||||
->where('caption', 'like', '%'.$tag.'%')
|
||||
->orWhere('uri', $tag)
|
||||
->orderBy('created_at', 'desc')
|
||||
->latest()
|
||||
->limit(10)
|
||||
->get();
|
||||
|
||||
if($posts->count() > 0) {
|
||||
|
@ -115,11 +136,9 @@ class SearchController extends Controller
|
|||
'thumb' => $item->thumb(),
|
||||
];
|
||||
});
|
||||
$tokens = $tokens->push($posts);
|
||||
}
|
||||
if($tokens->count() > 0) {
|
||||
$tokens = $tokens[0];
|
||||
$tokens['posts'] = $posts;
|
||||
}
|
||||
|
||||
return response()->json($tokens);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ return [
|
|||
| This value is the version of your PixelFed instance.
|
||||
|
|
||||
*/
|
||||
'version' => '0.8.4',
|
||||
'version' => '0.8.5',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
BIN
public/css/app.css
vendored
BIN
public/css/app.css
vendored
Binary file not shown.
BIN
public/css/appdark.css
vendored
BIN
public/css/appdark.css
vendored
Binary file not shown.
BIN
public/css/landing.css
vendored
Normal file
BIN
public/css/landing.css
vendored
Normal file
Binary file not shown.
BIN
public/js/app.js
vendored
BIN
public/js/app.js
vendored
Binary file not shown.
BIN
public/js/components.js
vendored
BIN
public/js/components.js
vendored
Binary file not shown.
BIN
public/js/compose.js
vendored
BIN
public/js/compose.js
vendored
Binary file not shown.
BIN
public/js/landing.js
vendored
BIN
public/js/landing.js
vendored
Binary file not shown.
BIN
public/js/profile.js
vendored
BIN
public/js/profile.js
vendored
Binary file not shown.
BIN
public/js/search.js
vendored
Normal file
BIN
public/js/search.js
vendored
Normal file
Binary file not shown.
BIN
public/js/status.js
vendored
BIN
public/js/status.js
vendored
Binary file not shown.
BIN
public/js/timeline.js
vendored
BIN
public/js/timeline.js
vendored
Binary file not shown.
Binary file not shown.
2
resources/assets/js/bootstrap.js
vendored
2
resources/assets/js/bootstrap.js
vendored
|
@ -3,8 +3,6 @@ window.Popper = require('popper.js').default;
|
|||
window.pixelfed = window.pixelfed || {};
|
||||
window.$ = window.jQuery = require('jquery');
|
||||
require('bootstrap');
|
||||
window.typeahead = require('./lib/typeahead');
|
||||
window.Bloodhound = require('./lib/bloodhound');
|
||||
window.axios = require('axios');
|
||||
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
|
||||
require('readmore-js');
|
||||
|
|
7
resources/assets/js/components.js
vendored
7
resources/assets/js/components.js
vendored
|
@ -38,7 +38,7 @@ import swal from 'sweetalert';
|
|||
|
||||
// require('./components/localstorage');
|
||||
// require('./components/commentform');
|
||||
require('./components/searchform');
|
||||
//require('./components/searchform');
|
||||
// require('./components/bookmarkform');
|
||||
// require('./components/statusform');
|
||||
//require('./components/embed');
|
||||
|
@ -63,6 +63,11 @@ require('./components/searchform');
|
|||
// Initialize Notification Helper
|
||||
window.pixelfed.n = {};
|
||||
|
||||
// Vue.component(
|
||||
// 'search-results',
|
||||
// require('./components/SearchResults.vue').default
|
||||
// );
|
||||
|
||||
// Vue.component(
|
||||
// 'photo-presenter',
|
||||
// require('./components/presenter/PhotoPresenter.vue').default
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,18 +1,3 @@
|
|||
<style scoped>
|
||||
.status-comments,
|
||||
.reactions,
|
||||
.col-md-4 {
|
||||
background: #fff;
|
||||
}
|
||||
.postPresenterContainer {
|
||||
background: #fff;
|
||||
}
|
||||
@media(min-width: 720px) {
|
||||
.postPresenterContainer {
|
||||
min-height: 600px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<div class="postComponent d-none">
|
||||
<div class="container px-0">
|
||||
|
@ -40,7 +25,7 @@
|
|||
<a class="dropdown-item font-weight-bold" v-on:click="blockProfile()">Block Profile</a>
|
||||
</div>
|
||||
<div v-if="ownerOrAdmin()">
|
||||
<!-- <a class="dropdown-item font-weight-bold" :href="editUrl()">Disable Comments</a> -->
|
||||
<a class="dropdown-item font-weight-bold" href="#" v-on:click.prevent="toggleCommentVisibility">{{ showComments ? 'Disable' : 'Enable'}} Comments</a>
|
||||
<a class="dropdown-item font-weight-bold" :href="editUrl()">Edit</a>
|
||||
<a class="dropdown-item font-weight-bold text-danger" v-on:click="deletePost(status)">Delete</a>
|
||||
</div>
|
||||
|
@ -92,19 +77,18 @@
|
|||
</a>
|
||||
<div class="float-right">
|
||||
<div class="post-actions">
|
||||
<div class="dropdown">
|
||||
<div v-if="user != false" class="dropdown">
|
||||
<button class="btn btn-link text-dark no-caret dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" title="Post options">
|
||||
<span class="fas fa-ellipsis-v text-muted"></span>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
|
||||
<span class="menu-user d-none">
|
||||
<span v-if="!owner()">
|
||||
<a class="dropdown-item font-weight-bold" :href="reportUrl()">Report</a>
|
||||
<a class="dropdown-item font-weight-bold" v-on:click="muteProfile">Mute Profile</a>
|
||||
<a class="dropdown-item font-weight-bold" v-on:click="blockProfile">Block Profile</a>
|
||||
</span>
|
||||
<span class="menu-author d-none">
|
||||
<!-- <a class="dropdown-item font-weight-bold" :href="editUrl()">Mute Comments</a>
|
||||
<a class="dropdown-item font-weight-bold" :href="editUrl()">Disable Comments</a> -->
|
||||
<span v-if="ownerOrAdmin()">
|
||||
<a class="dropdown-item font-weight-bold" href="#" v-on:click.prevent="toggleCommentVisibility">{{ showComments ? 'Disable' : 'Enable'}} Comments</a>
|
||||
<a class="dropdown-item font-weight-bold" :href="editUrl()">Edit</a>
|
||||
<a class="dropdown-item font-weight-bold text-danger" v-on:click="deletePost">Delete</a>
|
||||
</span>
|
||||
|
@ -114,19 +98,49 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="d-flex flex-md-column flex-column-reverse h-100">
|
||||
<div class="card-body status-comments">
|
||||
<div class="card-body status-comments pb-5">
|
||||
<div class="status-comment">
|
||||
<p class="mb-1 read-more" style="overflow: hidden;">
|
||||
<span class="font-weight-bold pr-1">{{statusUsername}}</span>
|
||||
<span class="comment-text" :id="status.id + '-status-readmore'" v-html="status.content"></span>
|
||||
</p>
|
||||
<post-comments :user="this.user" :post-id="statusId" :post-username="statusUsername"></post-comments>
|
||||
|
||||
<div v-if="showComments">
|
||||
<div class="postCommentsLoader text-center">
|
||||
<div class="spinner-border" role="status">
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="postCommentsContainer d-none pt-3">
|
||||
<p class="mb-1 text-center load-more-link d-none"><a href="#" class="text-muted" v-on:click="loadMore">Load more comments</a></p>
|
||||
<div class="comments" data-min-id="0" data-max-id="0">
|
||||
<div v-for="(reply, index) in results" class="pb-3">
|
||||
<p class="d-flex justify-content-between align-items-top read-more" style="overflow-y: hidden;">
|
||||
<span>
|
||||
<a class="text-dark font-weight-bold mr-1" :href="reply.account.url" v-bind:title="reply.account.username">{{truncate(reply.account.username,15)}}</a>
|
||||
<span class="text-break" v-html="reply.content"></span>
|
||||
</span>
|
||||
<span class="pl-2" style="min-width:38px">
|
||||
<span v-on:click="likeReply(reply, $event)"><i v-bind:class="[reply.favourited ? 'fas fa-heart fa-sm text-danger':'far fa-heart fa-sm text-lighter']"></i></span>
|
||||
<post-menu :status="reply" :profile="user" :size="'sm'" :modal="'true'" class="d-inline-block pl-2" v-on:deletePost="deleteComment(reply.id, index)"></post-menu>
|
||||
</span>
|
||||
</p>
|
||||
<p class="">
|
||||
<span class="text-muted mr-3" style="width: 20px;" v-text="timeAgo(reply.created_at)"></span>
|
||||
<span v-if="reply.favourites_count" class="text-muted comment-reaction font-weight-bold mr-3">{{reply.favourites_count == 1 ? '1 like' : reply.favourites_count + ' likes'}}</span>
|
||||
<span class="text-muted comment-reaction font-weight-bold cursor-pointer" v-on:click="replyFocus(reply)">Reply</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body flex-grow-0 py-1">
|
||||
<div class="reactions my-1">
|
||||
<h3 v-bind:class="[reactions.liked ? 'fas fa-heart text-danger pr-3 m-0 cursor-pointer' : 'far fa-heart pr-3 m-0 like-btn cursor-pointer']" title="Like" v-on:click="likeStatus"></h3>
|
||||
<h3 class="far fa-comment pr-3 m-0 cursor-pointer" title="Comment" v-on:click="commentFocus"></h3>
|
||||
<h3 class="far fa-comment pr-3 m-0 cursor-pointer" title="Comment" v-on:click="replyFocus(status)"></h3>
|
||||
<h3 v-bind:class="[reactions.shared ? 'far fa-share-square pr-3 m-0 text-primary cursor-pointer' : 'far fa-share-square pr-3 m-0 share-btn cursor-pointer']" title="Share" v-on:click="shareStatus"></h3>
|
||||
<h3 v-bind:class="[reactions.bookmarked ? 'fas fa-bookmark text-warning m-0 float-right cursor-pointer' : 'far fa-bookmark m-0 float-right cursor-pointer']" title="Bookmark" v-on:click="bookmarkStatus"></h3>
|
||||
</div>
|
||||
|
@ -145,15 +159,29 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer bg-white sticky-md-bottom">
|
||||
<div class="comment-form-guest">
|
||||
<div v-if="showComments && user.length !== 0" class="card-footer bg-white px-2 py-0">
|
||||
<ul class="nav align-items-center emoji-reactions" style="overflow-x: scroll;flex-wrap: unset;">
|
||||
<li class="nav-item" v-on:click="emojiReaction">😂</li>
|
||||
<li class="nav-item" v-on:click="emojiReaction">💯</li>
|
||||
<li class="nav-item" v-on:click="emojiReaction">❤️</li>
|
||||
<li class="nav-item" v-on:click="emojiReaction">🙌</li>
|
||||
<li class="nav-item" v-on:click="emojiReaction">👏</li>
|
||||
<li class="nav-item" v-on:click="emojiReaction">😍</li>
|
||||
<li class="nav-item" v-on:click="emojiReaction">😯</li>
|
||||
<li class="nav-item" v-on:click="emojiReaction">😢</li>
|
||||
<li class="nav-item" v-on:click="emojiReaction">😅</li>
|
||||
<li class="nav-item" v-on:click="emojiReaction">😁</li>
|
||||
<li class="nav-item" v-on:click="emojiReaction">🙂</li>
|
||||
<li class="nav-item" v-on:click="emojiReaction">😎</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-if="showComments" class="card-footer bg-white sticky-md-bottom p-0">
|
||||
<div v-if="user.length == 0" class="comment-form-guest p-3">
|
||||
<a href="/login">Login</a> to like or comment.
|
||||
</div>
|
||||
<form class="comment-form d-none" method="post" action="/i/comment" :data-id="statusId" data-truncate="false">
|
||||
<input type="hidden" name="_token" value="">
|
||||
<input type="hidden" name="item" :value="statusId">
|
||||
<input class="form-control" name="comment" placeholder="Add a comment…" autocomplete="off">
|
||||
<input type="submit" value="Send" class="btn btn-primary comment-submit" />
|
||||
<form v-else class="border-0 rounded-0 align-middle" method="post" action="/i/comment" :data-id="statusId" data-truncate="false">
|
||||
<textarea class="form-control border-0 rounded-0" name="comment" placeholder="Add a comment…" autocomplete="off" autocorrect="off" style="height:56px;line-height: 18px;max-height:80px;resize: none; padding-right:4.2rem;" v-model="replyText"></textarea>
|
||||
<input type="button" value="Post" class="d-inline-block btn btn-link font-weight-bold reply-btn text-decoration-none" v-on:click.prevent="postReply"/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -243,6 +271,68 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<style type="text/css" scoped>
|
||||
.status-comments,
|
||||
.reactions,
|
||||
.col-md-4 {
|
||||
background: #fff;
|
||||
}
|
||||
.postPresenterContainer {
|
||||
background: #fff;
|
||||
}
|
||||
@media(min-width: 720px) {
|
||||
.postPresenterContainer {
|
||||
min-height: 600px;
|
||||
}
|
||||
}
|
||||
::-webkit-scrollbar {
|
||||
width: 0px;
|
||||
background: transparent;
|
||||
}
|
||||
.reply-btn {
|
||||
position: absolute;
|
||||
bottom: 12px;
|
||||
right: 20px;
|
||||
width: 60px;
|
||||
text-align: center;
|
||||
border-radius: 0 3px 3px 0;
|
||||
}
|
||||
.text-lighter {
|
||||
color:#B8C2CC !important;
|
||||
}
|
||||
.text-break {
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
.comments p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.comment-reaction {
|
||||
font-size: 80%;
|
||||
}
|
||||
.show-reply-bar {
|
||||
display: inline-block;
|
||||
border-bottom: 1px solid #999;
|
||||
height: 0;
|
||||
margin-right: 16px;
|
||||
vertical-align: middle;
|
||||
width: 24px;
|
||||
}
|
||||
.comment-thread {
|
||||
margin: 4px 0 0 40px;
|
||||
width: calc(100% - 40px);
|
||||
}
|
||||
.emoji-reactions .nav-item {
|
||||
font-size: 1.2rem;
|
||||
padding: 7px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.emoji-reactions::-webkit-scrollbar {
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
background: transparent;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
pixelfed.postComponent = {};
|
||||
|
@ -262,7 +352,16 @@ export default {
|
|||
likesPage: 1,
|
||||
shares: [],
|
||||
sharesPage: 1,
|
||||
lightboxMedia: false
|
||||
lightboxMedia: false,
|
||||
replyText: '',
|
||||
|
||||
results: [],
|
||||
pagination: {},
|
||||
min_id: 0,
|
||||
max_id: 0,
|
||||
reply_to_profile_id: 0,
|
||||
thread: false,
|
||||
showComments: false
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -279,6 +378,8 @@ export default {
|
|||
updated() {
|
||||
$('.carousel').carousel();
|
||||
|
||||
pixelfed.readmore();
|
||||
|
||||
if(this.reactions) {
|
||||
if(this.reactions.bookmarked == true) {
|
||||
$('.postComponent .far.fa-bookmark').removeClass('far').addClass('fas text-warning');
|
||||
|
@ -345,6 +446,10 @@ export default {
|
|||
this.showMuteBlock();
|
||||
loader.hide();
|
||||
pixelfed.readmore();
|
||||
if(self.status.comments_disabled == false) {
|
||||
self.showComments = true;
|
||||
this.fetchComments();
|
||||
}
|
||||
$('.postComponent').removeClass('d-none');
|
||||
$('.postPresenterLoader').addClass('d-none');
|
||||
$('.postPresenterContainer').removeClass('d-none');
|
||||
|
@ -369,10 +474,6 @@ export default {
|
|||
});
|
||||
},
|
||||
|
||||
commentFocus() {
|
||||
$('.comment-form input[name="comment"]').focus();
|
||||
},
|
||||
|
||||
likesModal() {
|
||||
if(this.status.favourites_count == 0 || $('body').hasClass('loggedIn') == false) {
|
||||
return;
|
||||
|
@ -422,6 +523,7 @@ export default {
|
|||
|
||||
likeStatus(event) {
|
||||
if($('body').hasClass('loggedIn') == false) {
|
||||
window.location.href = '/login?next=' + encodeURIComponent(window.location.pathname);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -448,6 +550,7 @@ export default {
|
|||
|
||||
shareStatus() {
|
||||
if($('body').hasClass('loggedIn') == false) {
|
||||
window.location.href = '/login?next=' + encodeURIComponent(window.location.pathname);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -474,6 +577,7 @@ export default {
|
|||
|
||||
bookmarkStatus() {
|
||||
if($('body').hasClass('loggedIn') == false) {
|
||||
window.location.href = '/login?next=' + encodeURIComponent(window.location.pathname);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -521,6 +625,9 @@ export default {
|
|||
},
|
||||
|
||||
deletePost(status) {
|
||||
if(!this.ownerOrAdmin()) {
|
||||
return;
|
||||
}
|
||||
var result = confirm('Are you sure you want to delete this post?');
|
||||
if (result) {
|
||||
if($('body').hasClass('loggedIn') == false) {
|
||||
|
@ -553,6 +660,198 @@ export default {
|
|||
lightbox(src) {
|
||||
this.lightboxMedia = src;
|
||||
this.$refs.lightboxModal.show();
|
||||
},
|
||||
|
||||
postReply() {
|
||||
let self = this;
|
||||
if(this.replyText.length == 0 ||
|
||||
this.replyText.trim() == '@'+this.status.account.acct) {
|
||||
self.replyText = null;
|
||||
$('textarea[name="comment"]').blur();
|
||||
return;
|
||||
}
|
||||
let data = {
|
||||
item: this.statusId,
|
||||
comment: this.replyText
|
||||
}
|
||||
axios.post('/i/comment', data)
|
||||
.then(function(res) {
|
||||
let entity = res.data.entity;
|
||||
self.results.push(entity);
|
||||
self.replyText = '';
|
||||
let elem = $('.status-comments')[0];
|
||||
elem.scrollTop = elem.clientHeight;
|
||||
});
|
||||
},
|
||||
|
||||
deleteComment(id, i) {
|
||||
axios.post('/i/delete', {
|
||||
type: 'comment',
|
||||
item: id
|
||||
}).then(res => {
|
||||
this.results.splice(i, 1);
|
||||
}).catch(err => {
|
||||
swal('Something went wrong!', 'Please try again later', 'error');
|
||||
});
|
||||
},
|
||||
l(e) {
|
||||
let len = e.length;
|
||||
if(len < 10) { return e; }
|
||||
return e.substr(0, 10)+'...';
|
||||
},
|
||||
replyFocus(e) {
|
||||
this.reply_to_profile_id = e.account.id;
|
||||
this.replyText = '@' + e.account.username + ' ';
|
||||
$('textarea[name="comment"]').focus();
|
||||
},
|
||||
fetchComments() {
|
||||
let url = '/api/v2/comments/'+this.statusUsername+'/status/'+this.statusId;
|
||||
axios.get(url)
|
||||
.then(response => {
|
||||
let self = this;
|
||||
this.results = _.reverse(response.data.data);
|
||||
this.pagination = response.data.meta.pagination;
|
||||
if(this.results.length > 0) {
|
||||
$('.load-more-link').removeClass('d-none');
|
||||
}
|
||||
$('.postCommentsLoader').addClass('d-none');
|
||||
$('.postCommentsContainer').removeClass('d-none');
|
||||
}).catch(error => {
|
||||
if(!error.response) {
|
||||
$('.postCommentsLoader .lds-ring')
|
||||
.attr('style','width:100%')
|
||||
.addClass('pt-4 font-weight-bold text-muted')
|
||||
.text('An error occurred, cannot fetch comments. Please try again later.');
|
||||
} else {
|
||||
switch(error.response.status) {
|
||||
case 401:
|
||||
$('.postCommentsLoader .lds-ring')
|
||||
.attr('style','width:100%')
|
||||
.addClass('pt-4 font-weight-bold text-muted')
|
||||
.text('Please login to view.');
|
||||
break;
|
||||
|
||||
default:
|
||||
$('.postCommentsLoader .lds-ring')
|
||||
.attr('style','width:100%')
|
||||
.addClass('pt-4 font-weight-bold text-muted')
|
||||
.text('An error occurred, cannot fetch comments. Please try again later.');
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
loadMore(e) {
|
||||
e.preventDefault();
|
||||
if(this.pagination.total_pages == 1 || this.pagination.current_page == this.pagination.total_pages) {
|
||||
$('.load-more-link').addClass('d-none');
|
||||
return;
|
||||
}
|
||||
$('.postCommentsLoader').removeClass('d-none');
|
||||
let next = this.pagination.links.next;
|
||||
axios.get(next)
|
||||
.then(response => {
|
||||
let self = this;
|
||||
let res = response.data.data;
|
||||
$('.postCommentsLoader').addClass('d-none');
|
||||
for(let i=0; i < res.length; i++) {
|
||||
this.results.unshift(res[i]);
|
||||
}
|
||||
this.pagination = response.data.meta.pagination;
|
||||
});
|
||||
},
|
||||
likeReply(status, $event) {
|
||||
if($('body').hasClass('loggedIn') == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
axios.post('/i/like', {
|
||||
item: status.id
|
||||
}).then(res => {
|
||||
status.favourites_count = res.data.count;
|
||||
if(status.favourited == true) {
|
||||
status.favourited = false;
|
||||
} else {
|
||||
status.favourited = true;
|
||||
}
|
||||
}).catch(err => {
|
||||
swal('Error', 'Something went wrong, please try again later.', 'error');
|
||||
});
|
||||
},
|
||||
truncate(str,lim) {
|
||||
return _.truncate(str,{
|
||||
length: lim
|
||||
});
|
||||
},
|
||||
timeAgo(ts) {
|
||||
let date = Date.parse(ts);
|
||||
let seconds = Math.floor((new Date() - date) / 1000);
|
||||
let interval = Math.floor(seconds / 31536000);
|
||||
if (interval >= 1) {
|
||||
return interval + "y";
|
||||
}
|
||||
interval = Math.floor(seconds / 604800);
|
||||
if (interval >= 1) {
|
||||
return interval + "w";
|
||||
}
|
||||
interval = Math.floor(seconds / 86400);
|
||||
if (interval >= 1) {
|
||||
return interval + "d";
|
||||
}
|
||||
interval = Math.floor(seconds / 3600);
|
||||
if (interval >= 1) {
|
||||
return interval + "h";
|
||||
}
|
||||
interval = Math.floor(seconds / 60);
|
||||
if (interval >= 1) {
|
||||
return interval + "m";
|
||||
}
|
||||
return Math.floor(seconds) + "s";
|
||||
},
|
||||
|
||||
emojiReaction() {
|
||||
let em = event.target.innerText;
|
||||
if(this.replyText.length == 0) {
|
||||
this.reply_to_profile_id = this.status.account.id;
|
||||
this.replyText = '@' + this.status.account.username + ' ' + em;
|
||||
$('textarea[name="comment"]').focus();
|
||||
} else {
|
||||
this.reply_to_profile_id = this.status.account.id;
|
||||
this.replyText += em;
|
||||
$('textarea[name="comment"]').focus();
|
||||
}
|
||||
},
|
||||
|
||||
toggleCommentVisibility() {
|
||||
if(this.ownerOrAdmin() == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
let state = this.status.comments_disabled;
|
||||
let self = this;
|
||||
|
||||
if(state == true) {
|
||||
// re-enable comments
|
||||
axios.post('/i/visibility', {
|
||||
item: self.status.id,
|
||||
disableComments: false
|
||||
}).then(function(res) {
|
||||
window.location.href = self.status.url;
|
||||
}).catch(function(err) {
|
||||
return;
|
||||
});
|
||||
} else {
|
||||
// disable comments
|
||||
axios.post('/i/visibility', {
|
||||
item: self.status.id,
|
||||
disableComments: true
|
||||
}).then(function(res) {
|
||||
self.status.comments_disabled = false;
|
||||
self.showComments = false;
|
||||
}).catch(function(err) {
|
||||
return;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
|
|
@ -8,33 +8,56 @@
|
|||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-4 d-flex">
|
||||
<div class="profile-avatar mx-auto">
|
||||
<div class="profile-avatar mx-md-auto">
|
||||
<div class="d-block d-md-none">
|
||||
<div class="row">
|
||||
<div class="col-5">
|
||||
<img class="rounded-circle box-shadow mr-5" :src="profile.avatar" width="77px" height="77px">
|
||||
</div>
|
||||
<div class="col-7 pl-2">
|
||||
<p class="font-weight-ultralight h3 mb-0">{{profile.username}}</p>
|
||||
<p v-if="profile.id == user.id && user.hasOwnProperty('id')">
|
||||
<a class="btn btn-outline-dark py-0 px-4 mt-3" href="/settings/home">Edit Profile</a>
|
||||
</p>
|
||||
<div v-if="profile.id != user.id && user.hasOwnProperty('id')">
|
||||
<p class="mt-3 mb-0" v-if="relationship.following == true">
|
||||
<button type="button" class="btn btn-outline-dark font-weight-bold px-4 py-0" v-on:click="followProfile()" data-toggle="tooltip" title="Unfollow">Unfollow</button>
|
||||
</p>
|
||||
<p class="mt-3 mb-0" v-if="!relationship.following">
|
||||
<button type="button" class="btn btn-outline-dark font-weight-bold px-4 py-0" v-on:click="followProfile()" data-toggle="tooltip" title="Follow">Follow</button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-none d-md-block">
|
||||
<img class="rounded-circle box-shadow" :src="profile.avatar" width="172px" height="172px">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-8 d-flex align-items-center">
|
||||
<div class="profile-details">
|
||||
<div class="username-bar pb-2 d-flex align-items-center">
|
||||
<span class="font-weight-ultralight h1">{{profile.username}}</span>
|
||||
<div class="d-none d-md-flex username-bar pb-2 align-items-center">
|
||||
<span class="font-weight-ultralight h3">{{profile.username}}</span>
|
||||
<span class="pl-4" v-if="profile.is_admin">
|
||||
<span class="btn btn-outline-secondary font-weight-bold py-0">ADMIN</span>
|
||||
</span>
|
||||
<span class="pl-4">
|
||||
<a :href="'/users/'+profile.username+'.atom'" class="fas fa-rss fa-lg text-muted"></a>
|
||||
<a :href="'/users/'+profile.username+'.atom'" class="fas fa-rss fa-lg text-muted text-decoration-none"></a>
|
||||
</span>
|
||||
<span class="pl-4" v-if="owner">
|
||||
<a class="fas fa-cog fa-lg text-muted" href="/settings/home"></a>
|
||||
<a class="fas fa-cog fa-lg text-muted text-decoration-none" href="/settings/home"></a>
|
||||
</span>
|
||||
<span v-if="profile.id != user.id && user.hasOwnProperty('id')">
|
||||
<span class="pl-4" v-if="relationship.following == true">
|
||||
<button type="button" class="btn btn-outline-secondary font-weight-bold px-4 py-0" v-on:click="followProfile()">Unfollow</button>
|
||||
<button type="button" class="btn btn-outline-secondary font-weight-bold btn-sm" v-on:click="followProfile()" data-toggle="tooltip" title="Unfollow"><i class="fas fa-user-minus"></i></button>
|
||||
</span>
|
||||
<span class="pl-4" v-if="!relationship.following">
|
||||
<button type="button" class="btn btn-primary font-weight-bold px-4 py-0" v-on:click="followProfile()">Follow</button>
|
||||
<button type="button" class="btn btn-primary font-weight-bold btn-sm" v-on:click="followProfile()" data-toggle="tooltip" title="Follow"><i class="fas fa-user-plus"></i></button>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="profile-stats pb-3 d-inline-flex lead">
|
||||
<div class="d-none d-md-inline-flex profile-stats pb-3 lead">
|
||||
<div class="font-weight-light pr-5">
|
||||
<a class="text-dark" :href="profile.url">
|
||||
<span class="font-weight-bold">{{profile.statuses_count}}</span>
|
||||
|
@ -54,7 +77,7 @@
|
|||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<p class="lead mb-0 d-flex align-items-center">
|
||||
<p class="lead mb-0 d-flex align-items-center pt-3">
|
||||
<span class="font-weight-bold pr-3">{{profile.display_name}}</span>
|
||||
</p>
|
||||
<div v-if="profile.note" class="mb-0 lead" v-html="profile.note"></div>
|
||||
|
@ -64,22 +87,50 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="d-block d-md-none bg-white my-0 py-2 border-bottom">
|
||||
<ul class="nav d-flex justify-content-center">
|
||||
<li class="nav-item">
|
||||
<div class="font-weight-light">
|
||||
<span class="text-dark text-center">
|
||||
<p class="font-weight-bold mb-0">{{profile.statuses_count}}</p>
|
||||
<p class="text-muted mb-0">Posts</p>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav-item px-5">
|
||||
<div v-if="profileSettings.followers.count" class="font-weight-light">
|
||||
<a class="text-dark cursor-pointer text-center" v-on:click="followersModal()">
|
||||
<p class="font-weight-bold mb-0">{{profile.followers_count}}</p>
|
||||
<p class="text-muted mb-0">Followers</p>
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<div v-if="profileSettings.following.count" class="font-weight-light">
|
||||
<a class="text-dark cursor-pointer text-center" v-on:click="followingModal()">
|
||||
<p class="font-weight-bold mb-0">{{profile.following_count}}</p>
|
||||
<p class="text-muted mb-0">Following</p>
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="bg-white">
|
||||
<ul class="nav nav-topbar d-flex justify-content-center border-0">
|
||||
<!-- <li class="nav-item">
|
||||
<a class="nav-link active font-weight-bold text-uppercase" :href="profile.url">Posts</a>
|
||||
</li>
|
||||
-->
|
||||
<li class="nav-item">
|
||||
<a :class="this.mode == 'grid' ? 'nav-link font-weight-bold text-uppercase active' : 'nav-link font-weight-bold text-uppercase'" href="#" v-on:click.prevent="switchMode('grid')"><i class="fas fa-th"></i></a>
|
||||
<a :class="this.mode == 'grid' ? 'nav-link font-weight-bold text-uppercase text-primary' : 'nav-link font-weight-bold text-uppercase'" href="#" v-on:click.prevent="switchMode('grid')"><i class="fas fa-th fa-lg"></i></a>
|
||||
</li>
|
||||
|
||||
<!-- <li class="nav-item">
|
||||
<a :class="this.mode == 'masonry' ? 'nav-link font-weight-bold text-uppercase active' : 'nav-link font-weight-bold text-uppercase'" href="#" v-on:click.prevent="switchMode('masonry')"><i class="fas fa-th-large"></i></a>
|
||||
</li> -->
|
||||
|
||||
<li class="nav-item">
|
||||
<a :class="this.mode == 'list' ? 'nav-link font-weight-bold text-uppercase active' : 'nav-link font-weight-bold text-uppercase'" href="#" v-on:click.prevent="switchMode('list')"><i class="fas fa-th-list"></i></a>
|
||||
<li class="nav-item px-3">
|
||||
<a :class="this.mode == 'list' ? 'nav-link font-weight-bold text-uppercase text-primary' : 'nav-link font-weight-bold text-uppercase'" href="#" v-on:click.prevent="switchMode('list')"><i class="fas fa-th-list fa-lg"></i></a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item" v-if="owner">
|
||||
|
@ -89,7 +140,7 @@
|
|||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="profile-timeline mt-2 mt-md-4">
|
||||
<div class="profile-timeline mt-md-4">
|
||||
<div class="row" v-if="mode == 'grid'">
|
||||
<div class="col-4 p-0 p-sm-2 p-md-3" v-for="(s, index) in timeline">
|
||||
<a class="card info-overlay card-md-border-0" :href="s.url">
|
||||
|
@ -116,8 +167,8 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="row" v-if="mode == 'list'">
|
||||
<div class="col-md-8 col-lg-8 offset-md-2 pt-2 px-0 my-3 timeline">
|
||||
<div class="card mb-4 status-card card-md-rounded-0" :data-status-id="status.id" v-for="(status, index) in timeline" :key="status.id">
|
||||
<div class="col-md-8 col-lg-8 offset-md-2 px-0 mb-3 timeline">
|
||||
<div class="card status-card card-md-rounded-0" :data-status-id="status.id" v-for="(status, index) in timeline" :key="status.id">
|
||||
|
||||
<div class="card-header d-inline-flex align-items-center bg-white">
|
||||
<img v-bind:src="status.account.avatar" width="32px" height="32px" style="border-radius: 32px;">
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
<div v-if="!loading && !networkError" class="mt-5 row">
|
||||
|
||||
<div class="col-12 col-md-3">
|
||||
<div class="col-12 col-md-3 mb-4">
|
||||
<div>
|
||||
<p class="font-weight-bold">Filters</p>
|
||||
<div class="custom-control custom-checkbox">
|
||||
|
@ -29,14 +29,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-9">
|
||||
<p class="h3 font-weight-lighter">Showing results for <i>{{query}}</i></p>
|
||||
<p class="h5 font-weight-bold">Showing results for <i>{{query}}</i></p>
|
||||
<hr>
|
||||
|
||||
<div v-if="filters.hashtags && results.hashtags.length" class="row mb-4">
|
||||
<div v-if="filters.hashtags && results.hashtags" class="row mb-4">
|
||||
<p class="col-12 font-weight-bold text-muted">Hashtags</p>
|
||||
<a v-for="(hashtag, index) in results.hashtags" class="col-12 col-md-4" style="text-decoration: none;" :href="hashtag.url">
|
||||
<a v-for="(hashtag, index) in results.hashtags" class="col-12 col-md-3 mb-3" style="text-decoration: none;" :href="hashtag.url">
|
||||
<div class="card card-body text-center">
|
||||
<p class="lead mb-0 text-truncate text-dark">
|
||||
<p class="lead mb-0 text-truncate text-dark" data-toggle="tooltip" :title="hashtag.value">
|
||||
#{{hashtag.value}}
|
||||
</p>
|
||||
<p class="lead mb-0 small font-weight-bold text-dark">
|
||||
|
@ -46,28 +46,42 @@
|
|||
</a>
|
||||
</div>
|
||||
|
||||
<div v-if="filters.profiles && results.profiles.length" class="row mb-4">
|
||||
<div v-if="filters.profiles && results.profiles" class="row mb-4">
|
||||
<p class="col-12 font-weight-bold text-muted">Profiles</p>
|
||||
<a v-for="(profile, index) in results.profiles" class="col-12 col-md-4" style="text-decoration: none;" :href="profile.url">
|
||||
<div class="card card-body text-center border-left-primary">
|
||||
<p class="lead mb-0 text-truncate text-dark">
|
||||
<a v-for="(profile, index) in results.profiles" class="col-12 col-md-4 mb-3" style="text-decoration: none;" :href="profile.url">
|
||||
<div class="card card-body text-center">
|
||||
<p class="text-center">
|
||||
<img :src="profile.entity.thumb" width="32px" height="32px" class="rounded-circle box-shadow">
|
||||
</p>
|
||||
<p class="font-weight-bold text-truncate text-dark">
|
||||
{{profile.value}}
|
||||
</p>
|
||||
<!-- <p class="mb-0 text-center">
|
||||
<button :class="[profile.entity.following ? 'btn btn-secondary btn-sm py-1 font-weight-bold' : 'btn btn-primary btn-sm py-1 font-weight-bold']" v-on:click="followProfile(profile.entity.id)">
|
||||
{{profile.entity.following ? 'Unfollow' : 'Follow'}}
|
||||
</button>
|
||||
</p> -->
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div v-if="filters.statuses && results.statuses.length" class="row mb-4">
|
||||
<div v-if="filters.statuses && results.statuses" class="row mb-4">
|
||||
<p class="col-12 font-weight-bold text-muted">Statuses</p>
|
||||
<a v-for="(status, index) in results.statuses" class="col-12 col-md-4" style="text-decoration: none;" :href="status.url">
|
||||
<div class="card card-body text-center border-left-primary">
|
||||
<p class="lead mb-0 text-truncate text-dark">
|
||||
{{status.value}}
|
||||
<a v-for="(status, index) in results.statuses" class="col-12 col-md-4 mb-3" style="text-decoration: none;" :href="status.url">
|
||||
<div class="card">
|
||||
<img class="card-img-top img-fluid" :src="status.thumb" style="height:180px;">
|
||||
<div class="card-body text-center ">
|
||||
<p class="mb-0 small text-truncate font-weight-bold text-muted" v-html="status.value">
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div v-if="!results.hashtags && !results.profiles && !results.statuses">
|
||||
<p class="text-center lead">No results found!</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -81,7 +95,7 @@
|
|||
|
||||
<script type="text/javascript">
|
||||
export default {
|
||||
props: ['query'],
|
||||
props: ['query', 'profileId'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
|
@ -103,31 +117,35 @@ export default {
|
|||
this.fetchSearchResults();
|
||||
},
|
||||
mounted() {
|
||||
$('.search-form input').val(this.query);
|
||||
$('.search-bar input').val(this.query);
|
||||
},
|
||||
updated() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
fetchSearchResults() {
|
||||
axios.get('/api/search/' + this.query)
|
||||
axios.get('/api/search/' + encodeURI(this.query))
|
||||
.then(res => {
|
||||
let results = res.data;
|
||||
this.results.hashtags = results.filter(i => {
|
||||
return i.type == 'hashtag';
|
||||
});
|
||||
this.results.profiles = results.filter(i => {
|
||||
return i.type == 'profile';
|
||||
});
|
||||
this.results.statuses = results.filter(i => {
|
||||
return i.type == 'status';
|
||||
});
|
||||
this.results.hashtags = results.hashtags;
|
||||
this.results.profiles = results.profiles;
|
||||
this.results.statuses = results.posts;
|
||||
this.loading = false;
|
||||
}).catch(err => {
|
||||
this.loading = false;
|
||||
this.networkError = true;
|
||||
// this.networkError = true;
|
||||
})
|
||||
},
|
||||
|
||||
followProfile(id) {
|
||||
// todo: finish AP Accept handling to enable remote follows
|
||||
return;
|
||||
// axios.post('/i/follow', {
|
||||
// item: id
|
||||
// }).then(res => {
|
||||
// window.location.href = window.location.href;
|
||||
// });
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
|
71
resources/assets/js/components/searchform.js
vendored
71
resources/assets/js/components/searchform.js
vendored
|
@ -1,71 +0,0 @@
|
|||
$(document).ready(function() {
|
||||
|
||||
let queryEngine = new Bloodhound({
|
||||
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
|
||||
queryTokenizer: Bloodhound.tokenizers.whitespace,
|
||||
remote: {
|
||||
url: process.env.MIX_API_SEARCH + '/%QUERY%',
|
||||
wildcard: '%QUERY%'
|
||||
}
|
||||
});
|
||||
|
||||
$('.search-form .search-form-input').typeahead(null, {
|
||||
name: 'search',
|
||||
display: 'value',
|
||||
source: queryEngine,
|
||||
limit: 40,
|
||||
templates: {
|
||||
empty: [
|
||||
'<div class="alert alert-info mb-0 font-weight-bold">',
|
||||
'No Results Found',
|
||||
'</div>'
|
||||
].join('\n'),
|
||||
suggestion: function(data) {
|
||||
let type = data.type;
|
||||
let res = false;
|
||||
switch(type) {
|
||||
case 'hashtag':
|
||||
res = '<a href="'+data.url+'?src=search">' +
|
||||
'<div class="media d-flex align-items-center">' +
|
||||
'<div class="mr-3 h4 text-muted"><span class="fas fa-hashtag"></span></div>' +
|
||||
'<div class="media-body text-truncate">' +
|
||||
'<p class="mt-0 mb-0 font-weight-bold">'+data.value+'</p>' +
|
||||
'<p class="text-muted mb-0">'+data.count+' posts</p>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</a>';
|
||||
break;
|
||||
case 'profile':
|
||||
res = '<a href="'+data.url+'?src=search">' +
|
||||
'<div class="media d-flex align-items-center">' +
|
||||
'<div class="mr-3 h4 text-muted"><span class="far fa-user"></span></div>' +
|
||||
'<div class="media-body text-truncate">' +
|
||||
'<p class="mt-0 mb-0 font-weight-bold">'+data.name+'</p>' +
|
||||
'<p class="text-muted mb-0">'+data.value+'</p>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</a>';
|
||||
break;
|
||||
case 'status':
|
||||
res = '<a href="'+data.url+'?src=search">' +
|
||||
'<div class="media d-flex align-items-center">' +
|
||||
'<div class="mr-3 h4 text-muted"><img src="'+data.thumb+'" width="32px"></div>' +
|
||||
'<div class="media-body text-truncate">' +
|
||||
'<p class="mt-0 mb-0 font-weight-bold">'+data.name+'</p>' +
|
||||
'<p class="text-muted mb-0 small">'+data.value+'</p>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</a>';
|
||||
break;
|
||||
default:
|
||||
res = false;
|
||||
break;
|
||||
}
|
||||
if(res !== false) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
});
|
10
resources/assets/js/landing.js
vendored
10
resources/assets/js/landing.js
vendored
|
@ -1,10 +0,0 @@
|
|||
window.Vue = require('vue');
|
||||
|
||||
Vue.component(
|
||||
'landing-page',
|
||||
require('./components/LandingPage.vue').default
|
||||
);
|
||||
|
||||
new Vue({
|
||||
el: '#content'
|
||||
});
|
952
resources/assets/js/lib/bloodhound.js
vendored
952
resources/assets/js/lib/bloodhound.js
vendored
|
@ -1,952 +0,0 @@
|
|||
/*!
|
||||
* typeahead.js 1.2.0
|
||||
* https://github.com/twitter/typeahead.js
|
||||
* Copyright 2013-2017 Twitter, Inc. and other contributors; Licensed MIT
|
||||
*/
|
||||
|
||||
(function(root, factory) {
|
||||
if (typeof define === "function" && define.amd) {
|
||||
define([ "jquery" ], function(a0) {
|
||||
return root["Bloodhound"] = factory(a0);
|
||||
});
|
||||
} else if (typeof exports === "object") {
|
||||
module.exports = factory(require("jquery"));
|
||||
} else {
|
||||
root["Bloodhound"] = factory(root["jQuery"]);
|
||||
}
|
||||
})(this, function($) {
|
||||
var _ = function() {
|
||||
"use strict";
|
||||
return {
|
||||
isMsie: function() {
|
||||
return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false;
|
||||
},
|
||||
isBlankString: function(str) {
|
||||
return !str || /^\s*$/.test(str);
|
||||
},
|
||||
escapeRegExChars: function(str) {
|
||||
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
||||
},
|
||||
isString: function(obj) {
|
||||
return typeof obj === "string";
|
||||
},
|
||||
isNumber: function(obj) {
|
||||
return typeof obj === "number";
|
||||
},
|
||||
isArray: $.isArray,
|
||||
isFunction: $.isFunction,
|
||||
isObject: $.isPlainObject,
|
||||
isUndefined: function(obj) {
|
||||
return typeof obj === "undefined";
|
||||
},
|
||||
isElement: function(obj) {
|
||||
return !!(obj && obj.nodeType === 1);
|
||||
},
|
||||
isJQuery: function(obj) {
|
||||
return obj instanceof $;
|
||||
},
|
||||
toStr: function toStr(s) {
|
||||
return _.isUndefined(s) || s === null ? "" : s + "";
|
||||
},
|
||||
bind: $.proxy,
|
||||
each: function(collection, cb) {
|
||||
$.each(collection, reverseArgs);
|
||||
function reverseArgs(index, value) {
|
||||
return cb(value, index);
|
||||
}
|
||||
},
|
||||
map: $.map,
|
||||
filter: $.grep,
|
||||
every: function(obj, test) {
|
||||
var result = true;
|
||||
if (!obj) {
|
||||
return result;
|
||||
}
|
||||
$.each(obj, function(key, val) {
|
||||
if (!(result = test.call(null, val, key, obj))) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return !!result;
|
||||
},
|
||||
some: function(obj, test) {
|
||||
var result = false;
|
||||
if (!obj) {
|
||||
return result;
|
||||
}
|
||||
$.each(obj, function(key, val) {
|
||||
if (result = test.call(null, val, key, obj)) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return !!result;
|
||||
},
|
||||
mixin: $.extend,
|
||||
identity: function(x) {
|
||||
return x;
|
||||
},
|
||||
clone: function(obj) {
|
||||
return $.extend(true, {}, obj);
|
||||
},
|
||||
getIdGenerator: function() {
|
||||
var counter = 0;
|
||||
return function() {
|
||||
return counter++;
|
||||
};
|
||||
},
|
||||
templatify: function templatify(obj) {
|
||||
return $.isFunction(obj) ? obj : template;
|
||||
function template() {
|
||||
return String(obj);
|
||||
}
|
||||
},
|
||||
defer: function(fn) {
|
||||
setTimeout(fn, 0);
|
||||
},
|
||||
debounce: function(func, wait, immediate) {
|
||||
var timeout, result;
|
||||
return function() {
|
||||
var context = this, args = arguments, later, callNow;
|
||||
later = function() {
|
||||
timeout = null;
|
||||
if (!immediate) {
|
||||
result = func.apply(context, args);
|
||||
}
|
||||
};
|
||||
callNow = immediate && !timeout;
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
if (callNow) {
|
||||
result = func.apply(context, args);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
},
|
||||
throttle: function(func, wait) {
|
||||
var context, args, timeout, result, previous, later;
|
||||
previous = 0;
|
||||
later = function() {
|
||||
previous = new Date();
|
||||
timeout = null;
|
||||
result = func.apply(context, args);
|
||||
};
|
||||
return function() {
|
||||
var now = new Date(), remaining = wait - (now - previous);
|
||||
context = this;
|
||||
args = arguments;
|
||||
if (remaining <= 0) {
|
||||
clearTimeout(timeout);
|
||||
timeout = null;
|
||||
previous = now;
|
||||
result = func.apply(context, args);
|
||||
} else if (!timeout) {
|
||||
timeout = setTimeout(later, remaining);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
},
|
||||
stringify: function(val) {
|
||||
return _.isString(val) ? val : JSON.stringify(val);
|
||||
},
|
||||
guid: function() {
|
||||
function _p8(s) {
|
||||
var p = (Math.random().toString(16) + "000000000").substr(2, 8);
|
||||
return s ? "-" + p.substr(0, 4) + "-" + p.substr(4, 4) : p;
|
||||
}
|
||||
return "tt-" + _p8() + _p8(true) + _p8(true) + _p8();
|
||||
},
|
||||
noop: function() {}
|
||||
};
|
||||
}();
|
||||
var VERSION = "1.2.0";
|
||||
var tokenizers = function() {
|
||||
"use strict";
|
||||
return {
|
||||
nonword: nonword,
|
||||
whitespace: whitespace,
|
||||
ngram: ngram,
|
||||
obj: {
|
||||
nonword: getObjTokenizer(nonword),
|
||||
whitespace: getObjTokenizer(whitespace),
|
||||
ngram: getObjTokenizer(ngram)
|
||||
}
|
||||
};
|
||||
function whitespace(str) {
|
||||
str = _.toStr(str);
|
||||
return str ? str.split(/\s+/) : [];
|
||||
}
|
||||
function nonword(str) {
|
||||
str = _.toStr(str);
|
||||
return str ? str.split(/\W+/) : [];
|
||||
}
|
||||
function ngram(str) {
|
||||
str = _.toStr(str);
|
||||
var tokens = [], word = "";
|
||||
_.each(str.split(""), function(char) {
|
||||
if (char.match(/\s+/)) {
|
||||
word = "";
|
||||
} else {
|
||||
tokens.push(word + char);
|
||||
word += char;
|
||||
}
|
||||
});
|
||||
return tokens;
|
||||
}
|
||||
function getObjTokenizer(tokenizer) {
|
||||
return function setKey(keys) {
|
||||
keys = _.isArray(keys) ? keys : [].slice.call(arguments, 0);
|
||||
return function tokenize(o) {
|
||||
var tokens = [];
|
||||
_.each(keys, function(k) {
|
||||
tokens = tokens.concat(tokenizer(_.toStr(o[k])));
|
||||
});
|
||||
return tokens;
|
||||
};
|
||||
};
|
||||
}
|
||||
}();
|
||||
var LruCache = function() {
|
||||
"use strict";
|
||||
function LruCache(maxSize) {
|
||||
this.maxSize = _.isNumber(maxSize) ? maxSize : 100;
|
||||
this.reset();
|
||||
if (this.maxSize <= 0) {
|
||||
this.set = this.get = $.noop;
|
||||
}
|
||||
}
|
||||
_.mixin(LruCache.prototype, {
|
||||
set: function set(key, val) {
|
||||
var tailItem = this.list.tail, node;
|
||||
if (this.size >= this.maxSize) {
|
||||
this.list.remove(tailItem);
|
||||
delete this.hash[tailItem.key];
|
||||
this.size--;
|
||||
}
|
||||
if (node = this.hash[key]) {
|
||||
node.val = val;
|
||||
this.list.moveToFront(node);
|
||||
} else {
|
||||
node = new Node(key, val);
|
||||
this.list.add(node);
|
||||
this.hash[key] = node;
|
||||
this.size++;
|
||||
}
|
||||
},
|
||||
get: function get(key) {
|
||||
var node = this.hash[key];
|
||||
if (node) {
|
||||
this.list.moveToFront(node);
|
||||
return node.val;
|
||||
}
|
||||
},
|
||||
reset: function reset() {
|
||||
this.size = 0;
|
||||
this.hash = {};
|
||||
this.list = new List();
|
||||
}
|
||||
});
|
||||
function List() {
|
||||
this.head = this.tail = null;
|
||||
}
|
||||
_.mixin(List.prototype, {
|
||||
add: function add(node) {
|
||||
if (this.head) {
|
||||
node.next = this.head;
|
||||
this.head.prev = node;
|
||||
}
|
||||
this.head = node;
|
||||
this.tail = this.tail || node;
|
||||
},
|
||||
remove: function remove(node) {
|
||||
node.prev ? node.prev.next = node.next : this.head = node.next;
|
||||
node.next ? node.next.prev = node.prev : this.tail = node.prev;
|
||||
},
|
||||
moveToFront: function(node) {
|
||||
this.remove(node);
|
||||
this.add(node);
|
||||
}
|
||||
});
|
||||
function Node(key, val) {
|
||||
this.key = key;
|
||||
this.val = val;
|
||||
this.prev = this.next = null;
|
||||
}
|
||||
return LruCache;
|
||||
}();
|
||||
var PersistentStorage = function() {
|
||||
"use strict";
|
||||
var LOCAL_STORAGE;
|
||||
try {
|
||||
LOCAL_STORAGE = window.localStorage;
|
||||
LOCAL_STORAGE.setItem("~~~", "!");
|
||||
LOCAL_STORAGE.removeItem("~~~");
|
||||
} catch (err) {
|
||||
LOCAL_STORAGE = null;
|
||||
}
|
||||
function PersistentStorage(namespace, override) {
|
||||
this.prefix = [ "__", namespace, "__" ].join("");
|
||||
this.ttlKey = "__ttl__";
|
||||
this.keyMatcher = new RegExp("^" + _.escapeRegExChars(this.prefix));
|
||||
this.ls = override || LOCAL_STORAGE;
|
||||
!this.ls && this._noop();
|
||||
}
|
||||
_.mixin(PersistentStorage.prototype, {
|
||||
_prefix: function(key) {
|
||||
return this.prefix + key;
|
||||
},
|
||||
_ttlKey: function(key) {
|
||||
return this._prefix(key) + this.ttlKey;
|
||||
},
|
||||
_noop: function() {
|
||||
this.get = this.set = this.remove = this.clear = this.isExpired = _.noop;
|
||||
},
|
||||
_safeSet: function(key, val) {
|
||||
try {
|
||||
this.ls.setItem(key, val);
|
||||
} catch (err) {
|
||||
if (err.name === "QuotaExceededError") {
|
||||
this.clear();
|
||||
this._noop();
|
||||
}
|
||||
}
|
||||
},
|
||||
get: function(key) {
|
||||
if (this.isExpired(key)) {
|
||||
this.remove(key);
|
||||
}
|
||||
return decode(this.ls.getItem(this._prefix(key)));
|
||||
},
|
||||
set: function(key, val, ttl) {
|
||||
if (_.isNumber(ttl)) {
|
||||
this._safeSet(this._ttlKey(key), encode(now() + ttl));
|
||||
} else {
|
||||
this.ls.removeItem(this._ttlKey(key));
|
||||
}
|
||||
return this._safeSet(this._prefix(key), encode(val));
|
||||
},
|
||||
remove: function(key) {
|
||||
this.ls.removeItem(this._ttlKey(key));
|
||||
this.ls.removeItem(this._prefix(key));
|
||||
return this;
|
||||
},
|
||||
clear: function() {
|
||||
var i, keys = gatherMatchingKeys(this.keyMatcher);
|
||||
for (i = keys.length; i--; ) {
|
||||
this.remove(keys[i]);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
isExpired: function(key) {
|
||||
var ttl = decode(this.ls.getItem(this._ttlKey(key)));
|
||||
return _.isNumber(ttl) && now() > ttl ? true : false;
|
||||
}
|
||||
});
|
||||
return PersistentStorage;
|
||||
function now() {
|
||||
return new Date().getTime();
|
||||
}
|
||||
function encode(val) {
|
||||
return JSON.stringify(_.isUndefined(val) ? null : val);
|
||||
}
|
||||
function decode(val) {
|
||||
return $.parseJSON(val);
|
||||
}
|
||||
function gatherMatchingKeys(keyMatcher) {
|
||||
var i, key, keys = [], len = LOCAL_STORAGE.length;
|
||||
for (i = 0; i < len; i++) {
|
||||
if ((key = LOCAL_STORAGE.key(i)).match(keyMatcher)) {
|
||||
keys.push(key.replace(keyMatcher, ""));
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
}();
|
||||
var Transport = function() {
|
||||
"use strict";
|
||||
var pendingRequestsCount = 0, pendingRequests = {}, sharedCache = new LruCache(10);
|
||||
function Transport(o) {
|
||||
o = o || {};
|
||||
this.maxPendingRequests = o.maxPendingRequests || 6;
|
||||
this.cancelled = false;
|
||||
this.lastReq = null;
|
||||
this._send = o.transport;
|
||||
this._get = o.limiter ? o.limiter(this._get) : this._get;
|
||||
this._cache = o.cache === false ? new LruCache(0) : sharedCache;
|
||||
}
|
||||
Transport.setMaxPendingRequests = function setMaxPendingRequests(num) {
|
||||
this.maxPendingRequests = num;
|
||||
};
|
||||
Transport.resetCache = function resetCache() {
|
||||
sharedCache.reset();
|
||||
};
|
||||
_.mixin(Transport.prototype, {
|
||||
_fingerprint: function fingerprint(o) {
|
||||
o = o || {};
|
||||
return o.url + o.type + $.param(o.data || {});
|
||||
},
|
||||
_get: function(o, cb) {
|
||||
var that = this, fingerprint, jqXhr;
|
||||
fingerprint = this._fingerprint(o);
|
||||
if (this.cancelled || fingerprint !== this.lastReq) {
|
||||
return;
|
||||
}
|
||||
if (jqXhr = pendingRequests[fingerprint]) {
|
||||
jqXhr.done(done).fail(fail);
|
||||
} else if (pendingRequestsCount < this.maxPendingRequests) {
|
||||
pendingRequestsCount++;
|
||||
pendingRequests[fingerprint] = this._send(o).done(done).fail(fail).always(always);
|
||||
} else {
|
||||
this.onDeckRequestArgs = [].slice.call(arguments, 0);
|
||||
}
|
||||
function done(resp) {
|
||||
cb(null, resp);
|
||||
that._cache.set(fingerprint, resp);
|
||||
}
|
||||
function fail() {
|
||||
cb(true);
|
||||
}
|
||||
function always() {
|
||||
pendingRequestsCount--;
|
||||
delete pendingRequests[fingerprint];
|
||||
if (that.onDeckRequestArgs) {
|
||||
that._get.apply(that, that.onDeckRequestArgs);
|
||||
that.onDeckRequestArgs = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
get: function(o, cb) {
|
||||
var resp, fingerprint;
|
||||
cb = cb || $.noop;
|
||||
o = _.isString(o) ? {
|
||||
url: o
|
||||
} : o || {};
|
||||
fingerprint = this._fingerprint(o);
|
||||
this.cancelled = false;
|
||||
this.lastReq = fingerprint;
|
||||
if (resp = this._cache.get(fingerprint)) {
|
||||
cb(null, resp);
|
||||
} else {
|
||||
this._get(o, cb);
|
||||
}
|
||||
},
|
||||
cancel: function() {
|
||||
this.cancelled = true;
|
||||
}
|
||||
});
|
||||
return Transport;
|
||||
}();
|
||||
var SearchIndex = window.SearchIndex = function() {
|
||||
"use strict";
|
||||
var CHILDREN = "c", IDS = "i";
|
||||
function SearchIndex(o) {
|
||||
o = o || {};
|
||||
if (!o.datumTokenizer || !o.queryTokenizer) {
|
||||
$.error("datumTokenizer and queryTokenizer are both required");
|
||||
}
|
||||
this.identify = o.identify || _.stringify;
|
||||
this.datumTokenizer = o.datumTokenizer;
|
||||
this.queryTokenizer = o.queryTokenizer;
|
||||
this.matchAnyQueryToken = o.matchAnyQueryToken;
|
||||
this.reset();
|
||||
}
|
||||
_.mixin(SearchIndex.prototype, {
|
||||
bootstrap: function bootstrap(o) {
|
||||
this.datums = o.datums;
|
||||
this.trie = o.trie;
|
||||
},
|
||||
add: function(data) {
|
||||
var that = this;
|
||||
data = _.isArray(data) ? data : [ data ];
|
||||
_.each(data, function(datum) {
|
||||
var id, tokens;
|
||||
that.datums[id = that.identify(datum)] = datum;
|
||||
tokens = normalizeTokens(that.datumTokenizer(datum));
|
||||
_.each(tokens, function(token) {
|
||||
var node, chars, ch;
|
||||
node = that.trie;
|
||||
chars = token.split("");
|
||||
while (ch = chars.shift()) {
|
||||
node = node[CHILDREN][ch] || (node[CHILDREN][ch] = newNode());
|
||||
node[IDS].push(id);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
get: function get(ids) {
|
||||
var that = this;
|
||||
return _.map(ids, function(id) {
|
||||
return that.datums[id];
|
||||
});
|
||||
},
|
||||
search: function search(query) {
|
||||
var that = this, tokens, matches;
|
||||
tokens = normalizeTokens(this.queryTokenizer(query));
|
||||
_.each(tokens, function(token) {
|
||||
var node, chars, ch, ids;
|
||||
if (matches && matches.length === 0 && !that.matchAnyQueryToken) {
|
||||
return false;
|
||||
}
|
||||
node = that.trie;
|
||||
chars = token.split("");
|
||||
while (node && (ch = chars.shift())) {
|
||||
node = node[CHILDREN][ch];
|
||||
}
|
||||
if (node && chars.length === 0) {
|
||||
ids = node[IDS].slice(0);
|
||||
matches = matches ? getIntersection(matches, ids) : ids;
|
||||
} else {
|
||||
if (!that.matchAnyQueryToken) {
|
||||
matches = [];
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
return matches ? _.map(unique(matches), function(id) {
|
||||
return that.datums[id];
|
||||
}) : [];
|
||||
},
|
||||
all: function all() {
|
||||
var values = [];
|
||||
for (var key in this.datums) {
|
||||
values.push(this.datums[key]);
|
||||
}
|
||||
return values;
|
||||
},
|
||||
reset: function reset() {
|
||||
this.datums = {};
|
||||
this.trie = newNode();
|
||||
},
|
||||
serialize: function serialize() {
|
||||
return {
|
||||
datums: this.datums,
|
||||
trie: this.trie
|
||||
};
|
||||
}
|
||||
});
|
||||
return SearchIndex;
|
||||
function normalizeTokens(tokens) {
|
||||
tokens = _.filter(tokens, function(token) {
|
||||
return !!token;
|
||||
});
|
||||
tokens = _.map(tokens, function(token) {
|
||||
return token.toLowerCase();
|
||||
});
|
||||
return tokens;
|
||||
}
|
||||
function newNode() {
|
||||
var node = {};
|
||||
node[IDS] = [];
|
||||
node[CHILDREN] = {};
|
||||
return node;
|
||||
}
|
||||
function unique(array) {
|
||||
var seen = {}, uniques = [];
|
||||
for (var i = 0, len = array.length; i < len; i++) {
|
||||
if (!seen[array[i]]) {
|
||||
seen[array[i]] = true;
|
||||
uniques.push(array[i]);
|
||||
}
|
||||
}
|
||||
return uniques;
|
||||
}
|
||||
function getIntersection(arrayA, arrayB) {
|
||||
var ai = 0, bi = 0, intersection = [];
|
||||
arrayA = arrayA.sort();
|
||||
arrayB = arrayB.sort();
|
||||
var lenArrayA = arrayA.length, lenArrayB = arrayB.length;
|
||||
while (ai < lenArrayA && bi < lenArrayB) {
|
||||
if (arrayA[ai] < arrayB[bi]) {
|
||||
ai++;
|
||||
} else if (arrayA[ai] > arrayB[bi]) {
|
||||
bi++;
|
||||
} else {
|
||||
intersection.push(arrayA[ai]);
|
||||
ai++;
|
||||
bi++;
|
||||
}
|
||||
}
|
||||
return intersection;
|
||||
}
|
||||
}();
|
||||
var Prefetch = function() {
|
||||
"use strict";
|
||||
var keys;
|
||||
keys = {
|
||||
data: "data",
|
||||
protocol: "protocol",
|
||||
thumbprint: "thumbprint"
|
||||
};
|
||||
function Prefetch(o) {
|
||||
this.url = o.url;
|
||||
this.ttl = o.ttl;
|
||||
this.cache = o.cache;
|
||||
this.prepare = o.prepare;
|
||||
this.transform = o.transform;
|
||||
this.transport = o.transport;
|
||||
this.thumbprint = o.thumbprint;
|
||||
this.storage = new PersistentStorage(o.cacheKey);
|
||||
}
|
||||
_.mixin(Prefetch.prototype, {
|
||||
_settings: function settings() {
|
||||
return {
|
||||
url: this.url,
|
||||
type: "GET",
|
||||
dataType: "json"
|
||||
};
|
||||
},
|
||||
store: function store(data) {
|
||||
if (!this.cache) {
|
||||
return;
|
||||
}
|
||||
this.storage.set(keys.data, data, this.ttl);
|
||||
this.storage.set(keys.protocol, location.protocol, this.ttl);
|
||||
this.storage.set(keys.thumbprint, this.thumbprint, this.ttl);
|
||||
},
|
||||
fromCache: function fromCache() {
|
||||
var stored = {}, isExpired;
|
||||
if (!this.cache) {
|
||||
return null;
|
||||
}
|
||||
stored.data = this.storage.get(keys.data);
|
||||
stored.protocol = this.storage.get(keys.protocol);
|
||||
stored.thumbprint = this.storage.get(keys.thumbprint);
|
||||
isExpired = stored.thumbprint !== this.thumbprint || stored.protocol !== location.protocol;
|
||||
return stored.data && !isExpired ? stored.data : null;
|
||||
},
|
||||
fromNetwork: function(cb) {
|
||||
var that = this, settings;
|
||||
if (!cb) {
|
||||
return;
|
||||
}
|
||||
settings = this.prepare(this._settings());
|
||||
this.transport(settings).fail(onError).done(onResponse);
|
||||
function onError() {
|
||||
cb(true);
|
||||
}
|
||||
function onResponse(resp) {
|
||||
cb(null, that.transform(resp));
|
||||
}
|
||||
},
|
||||
clear: function clear() {
|
||||
this.storage.clear();
|
||||
return this;
|
||||
}
|
||||
});
|
||||
return Prefetch;
|
||||
}();
|
||||
var Remote = function() {
|
||||
"use strict";
|
||||
function Remote(o) {
|
||||
this.url = o.url;
|
||||
this.prepare = o.prepare;
|
||||
this.transform = o.transform;
|
||||
this.indexResponse = o.indexResponse;
|
||||
this.transport = new Transport({
|
||||
cache: o.cache,
|
||||
limiter: o.limiter,
|
||||
transport: o.transport,
|
||||
maxPendingRequests: o.maxPendingRequests
|
||||
});
|
||||
}
|
||||
_.mixin(Remote.prototype, {
|
||||
_settings: function settings() {
|
||||
return {
|
||||
url: this.url,
|
||||
type: "GET",
|
||||
dataType: "json"
|
||||
};
|
||||
},
|
||||
get: function get(query, cb) {
|
||||
var that = this, settings;
|
||||
if (!cb) {
|
||||
return;
|
||||
}
|
||||
query = query || "";
|
||||
settings = this.prepare(query, this._settings());
|
||||
return this.transport.get(settings, onResponse);
|
||||
function onResponse(err, resp) {
|
||||
err ? cb([]) : cb(that.transform(resp));
|
||||
}
|
||||
},
|
||||
cancelLastRequest: function cancelLastRequest() {
|
||||
this.transport.cancel();
|
||||
}
|
||||
});
|
||||
return Remote;
|
||||
}();
|
||||
var oParser = function() {
|
||||
"use strict";
|
||||
return function parse(o) {
|
||||
var defaults, sorter;
|
||||
defaults = {
|
||||
initialize: true,
|
||||
identify: _.stringify,
|
||||
datumTokenizer: null,
|
||||
queryTokenizer: null,
|
||||
matchAnyQueryToken: false,
|
||||
sufficient: 5,
|
||||
indexRemote: false,
|
||||
sorter: null,
|
||||
local: [],
|
||||
prefetch: null,
|
||||
remote: null
|
||||
};
|
||||
o = _.mixin(defaults, o || {});
|
||||
!o.datumTokenizer && $.error("datumTokenizer is required");
|
||||
!o.queryTokenizer && $.error("queryTokenizer is required");
|
||||
sorter = o.sorter;
|
||||
o.sorter = sorter ? function(x) {
|
||||
return x.sort(sorter);
|
||||
} : _.identity;
|
||||
o.local = _.isFunction(o.local) ? o.local() : o.local;
|
||||
o.prefetch = parsePrefetch(o.prefetch);
|
||||
o.remote = parseRemote(o.remote);
|
||||
return o;
|
||||
};
|
||||
function parsePrefetch(o) {
|
||||
var defaults;
|
||||
if (!o) {
|
||||
return null;
|
||||
}
|
||||
defaults = {
|
||||
url: null,
|
||||
ttl: 24 * 60 * 60 * 1e3,
|
||||
cache: true,
|
||||
cacheKey: null,
|
||||
thumbprint: "",
|
||||
prepare: _.identity,
|
||||
transform: _.identity,
|
||||
transport: null
|
||||
};
|
||||
o = _.isString(o) ? {
|
||||
url: o
|
||||
} : o;
|
||||
o = _.mixin(defaults, o);
|
||||
!o.url && $.error("prefetch requires url to be set");
|
||||
o.transform = o.filter || o.transform;
|
||||
o.cacheKey = o.cacheKey || o.url;
|
||||
o.thumbprint = VERSION + o.thumbprint;
|
||||
o.transport = o.transport ? callbackToDeferred(o.transport) : $.ajax;
|
||||
return o;
|
||||
}
|
||||
function parseRemote(o) {
|
||||
var defaults;
|
||||
if (!o) {
|
||||
return;
|
||||
}
|
||||
defaults = {
|
||||
url: null,
|
||||
cache: true,
|
||||
prepare: null,
|
||||
replace: null,
|
||||
wildcard: null,
|
||||
limiter: null,
|
||||
rateLimitBy: "debounce",
|
||||
rateLimitWait: 300,
|
||||
transform: _.identity,
|
||||
transport: null
|
||||
};
|
||||
o = _.isString(o) ? {
|
||||
url: o
|
||||
} : o;
|
||||
o = _.mixin(defaults, o);
|
||||
!o.url && $.error("remote requires url to be set");
|
||||
o.transform = o.filter || o.transform;
|
||||
o.prepare = toRemotePrepare(o);
|
||||
o.limiter = toLimiter(o);
|
||||
o.transport = o.transport ? callbackToDeferred(o.transport) : $.ajax;
|
||||
delete o.replace;
|
||||
delete o.wildcard;
|
||||
delete o.rateLimitBy;
|
||||
delete o.rateLimitWait;
|
||||
return o;
|
||||
}
|
||||
function toRemotePrepare(o) {
|
||||
var prepare, replace, wildcard;
|
||||
prepare = o.prepare;
|
||||
replace = o.replace;
|
||||
wildcard = o.wildcard;
|
||||
if (prepare) {
|
||||
return prepare;
|
||||
}
|
||||
if (replace) {
|
||||
prepare = prepareByReplace;
|
||||
} else if (o.wildcard) {
|
||||
prepare = prepareByWildcard;
|
||||
} else {
|
||||
prepare = identityPrepare;
|
||||
}
|
||||
return prepare;
|
||||
function prepareByReplace(query, settings) {
|
||||
settings.url = replace(settings.url, query);
|
||||
return settings;
|
||||
}
|
||||
function prepareByWildcard(query, settings) {
|
||||
settings.url = settings.url.replace(wildcard, encodeURIComponent(query));
|
||||
return settings;
|
||||
}
|
||||
function identityPrepare(query, settings) {
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
function toLimiter(o) {
|
||||
var limiter, method, wait;
|
||||
limiter = o.limiter;
|
||||
method = o.rateLimitBy;
|
||||
wait = o.rateLimitWait;
|
||||
if (!limiter) {
|
||||
limiter = /^throttle$/i.test(method) ? throttle(wait) : debounce(wait);
|
||||
}
|
||||
return limiter;
|
||||
function debounce(wait) {
|
||||
return function debounce(fn) {
|
||||
return _.debounce(fn, wait);
|
||||
};
|
||||
}
|
||||
function throttle(wait) {
|
||||
return function throttle(fn) {
|
||||
return _.throttle(fn, wait);
|
||||
};
|
||||
}
|
||||
}
|
||||
function callbackToDeferred(fn) {
|
||||
return function wrapper(o) {
|
||||
var deferred = $.Deferred();
|
||||
fn(o, onSuccess, onError);
|
||||
return deferred;
|
||||
function onSuccess(resp) {
|
||||
_.defer(function() {
|
||||
deferred.resolve(resp);
|
||||
});
|
||||
}
|
||||
function onError(err) {
|
||||
_.defer(function() {
|
||||
deferred.reject(err);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
}();
|
||||
var Bloodhound = function() {
|
||||
"use strict";
|
||||
var old;
|
||||
old = window && window.Bloodhound;
|
||||
function Bloodhound(o) {
|
||||
o = oParser(o);
|
||||
this.sorter = o.sorter;
|
||||
this.identify = o.identify;
|
||||
this.sufficient = o.sufficient;
|
||||
this.indexRemote = o.indexRemote;
|
||||
this.local = o.local;
|
||||
this.remote = o.remote ? new Remote(o.remote) : null;
|
||||
this.prefetch = o.prefetch ? new Prefetch(o.prefetch) : null;
|
||||
this.index = new SearchIndex({
|
||||
identify: this.identify,
|
||||
datumTokenizer: o.datumTokenizer,
|
||||
queryTokenizer: o.queryTokenizer
|
||||
});
|
||||
o.initialize !== false && this.initialize();
|
||||
}
|
||||
Bloodhound.noConflict = function noConflict() {
|
||||
window && (window.Bloodhound = old);
|
||||
return Bloodhound;
|
||||
};
|
||||
Bloodhound.tokenizers = tokenizers;
|
||||
_.mixin(Bloodhound.prototype, {
|
||||
__ttAdapter: function ttAdapter() {
|
||||
var that = this;
|
||||
return this.remote ? withAsync : withoutAsync;
|
||||
function withAsync(query, sync, async) {
|
||||
return that.search(query, sync, async);
|
||||
}
|
||||
function withoutAsync(query, sync) {
|
||||
return that.search(query, sync);
|
||||
}
|
||||
},
|
||||
_loadPrefetch: function loadPrefetch() {
|
||||
var that = this, deferred, serialized;
|
||||
deferred = $.Deferred();
|
||||
if (!this.prefetch) {
|
||||
deferred.resolve();
|
||||
} else if (serialized = this.prefetch.fromCache()) {
|
||||
this.index.bootstrap(serialized);
|
||||
deferred.resolve();
|
||||
} else {
|
||||
this.prefetch.fromNetwork(done);
|
||||
}
|
||||
return deferred.promise();
|
||||
function done(err, data) {
|
||||
if (err) {
|
||||
return deferred.reject();
|
||||
}
|
||||
that.add(data);
|
||||
that.prefetch.store(that.index.serialize());
|
||||
deferred.resolve();
|
||||
}
|
||||
},
|
||||
_initialize: function initialize() {
|
||||
var that = this, deferred;
|
||||
this.clear();
|
||||
(this.initPromise = this._loadPrefetch()).done(addLocalToIndex);
|
||||
return this.initPromise;
|
||||
function addLocalToIndex() {
|
||||
that.add(that.local);
|
||||
}
|
||||
},
|
||||
initialize: function initialize(force) {
|
||||
return !this.initPromise || force ? this._initialize() : this.initPromise;
|
||||
},
|
||||
add: function add(data) {
|
||||
this.index.add(data);
|
||||
return this;
|
||||
},
|
||||
get: function get(ids) {
|
||||
ids = _.isArray(ids) ? ids : [].slice.call(arguments);
|
||||
return this.index.get(ids);
|
||||
},
|
||||
search: function search(query, sync, async) {
|
||||
var that = this, local;
|
||||
sync = sync || _.noop;
|
||||
async = async || _.noop;
|
||||
local = this.sorter(this.index.search(query));
|
||||
sync(this.remote ? local.slice() : local);
|
||||
if (this.remote && local.length < this.sufficient) {
|
||||
this.remote.get(query, processRemote);
|
||||
} else if (this.remote) {
|
||||
this.remote.cancelLastRequest();
|
||||
}
|
||||
return this;
|
||||
function processRemote(remote) {
|
||||
var nonDuplicates = [];
|
||||
_.each(remote, function(r) {
|
||||
!_.some(local, function(l) {
|
||||
return that.identify(r) === that.identify(l);
|
||||
}) && nonDuplicates.push(r);
|
||||
});
|
||||
that.indexRemote && that.add(nonDuplicates);
|
||||
async(nonDuplicates);
|
||||
}
|
||||
},
|
||||
all: function all() {
|
||||
return this.index.all();
|
||||
},
|
||||
clear: function clear() {
|
||||
this.index.reset();
|
||||
return this;
|
||||
},
|
||||
clearPrefetchCache: function clearPrefetchCache() {
|
||||
this.prefetch && this.prefetch.clear();
|
||||
return this;
|
||||
},
|
||||
clearRemoteCache: function clearRemoteCache() {
|
||||
Transport.resetCache();
|
||||
return this;
|
||||
},
|
||||
ttAdapter: function ttAdapter() {
|
||||
return this.__ttAdapter();
|
||||
}
|
||||
});
|
||||
return Bloodhound;
|
||||
}();
|
||||
return Bloodhound;
|
||||
});
|
1674
resources/assets/js/lib/typeahead.js
vendored
1674
resources/assets/js/lib/typeahead.js
vendored
File diff suppressed because it is too large
Load diff
4
resources/assets/js/search.js
vendored
Normal file
4
resources/assets/js/search.js
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
Vue.component(
|
||||
'search-results',
|
||||
require('./components/SearchResults.vue').default
|
||||
);
|
10
resources/assets/sass/custom.scss
vendored
10
resources/assets/sass/custom.scss
vendored
|
@ -37,16 +37,6 @@ body, button, input, textarea {
|
|||
color: #212529 !important;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.search-form input,
|
||||
.search-form .form-inline,
|
||||
.search-form .form-control {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.settings-nav .active {
|
||||
border-left: 2px solid #6c757d !important
|
||||
}
|
||||
|
|
13
resources/assets/sass/landing.scss
vendored
Normal file
13
resources/assets/sass/landing.scss
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Landing Page bundle
|
||||
|
||||
@import 'variables';
|
||||
@import '~bootstrap/scss/bootstrap';
|
||||
@import 'custom';
|
||||
@import 'landing/carousel';
|
||||
@import 'landing/devices';
|
||||
|
||||
.container.slim {
|
||||
width: auto;
|
||||
max-width: 680px;
|
||||
padding: 0 15px;
|
||||
}
|
126
resources/assets/sass/landing/carousel.scss
vendored
Normal file
126
resources/assets/sass/landing/carousel.scss
vendored
Normal file
|
@ -0,0 +1,126 @@
|
|||
@-webkit-keyframes iosDeviceCarousel {
|
||||
0% {
|
||||
opacity:1;
|
||||
}
|
||||
17% {
|
||||
opacity:1;
|
||||
}
|
||||
25% {
|
||||
opacity:0;
|
||||
}
|
||||
92% {
|
||||
opacity:0;
|
||||
}
|
||||
100% {
|
||||
opacity:1;
|
||||
}
|
||||
}
|
||||
|
||||
@-moz-keyframes iosDeviceCarousel {
|
||||
0% {
|
||||
opacity:1;
|
||||
}
|
||||
17% {
|
||||
opacity:1;
|
||||
}
|
||||
25% {
|
||||
opacity:0;
|
||||
}
|
||||
92% {
|
||||
opacity:0;
|
||||
}
|
||||
100% {
|
||||
opacity:1;
|
||||
}
|
||||
}
|
||||
|
||||
@-o-keyframes iosDeviceCarousel {
|
||||
0% {
|
||||
opacity:1;
|
||||
}
|
||||
17% {
|
||||
opacity:1;
|
||||
}
|
||||
25% {
|
||||
opacity:0;
|
||||
}
|
||||
92% {
|
||||
opacity:0;
|
||||
}
|
||||
100% {
|
||||
opacity:1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes iosDeviceCarousel {
|
||||
0% {
|
||||
opacity:1;
|
||||
}
|
||||
17% {
|
||||
opacity:1;
|
||||
}
|
||||
25% {
|
||||
opacity:0;
|
||||
}
|
||||
92% {
|
||||
opacity:0;
|
||||
}
|
||||
100% {
|
||||
opacity:1;
|
||||
}
|
||||
}
|
||||
|
||||
#iosDevice {
|
||||
position:relative;
|
||||
margin:0 auto;
|
||||
}
|
||||
#iosDevice img {
|
||||
position:absolute;
|
||||
left:0;
|
||||
}
|
||||
|
||||
#iosDevice img {
|
||||
-webkit-animation-name: iosDeviceCarousel;
|
||||
-webkit-animation-timing-function: ease-in-out;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
-webkit-animation-duration: 16s;
|
||||
|
||||
-moz-animation-name: iosDeviceCarousel;
|
||||
-moz-animation-timing-function: ease-in-out;
|
||||
-moz-animation-iteration-count: infinite;
|
||||
-moz-animation-duration: 16s;
|
||||
|
||||
-o-animation-name: iosDeviceCarousel;
|
||||
-o-animation-timing-function: ease-in-out;
|
||||
-o-animation-iteration-count: infinite;
|
||||
-o-animation-duration: 16s;
|
||||
|
||||
animation-name: iosDeviceCarousel;
|
||||
animation-timing-function: ease-in-out;
|
||||
animation-iteration-count: infinite;
|
||||
animation-duration: 16s;
|
||||
}
|
||||
#iosDevice img:nth-of-type(1) {
|
||||
-webkit-animation-delay: 12s;
|
||||
-moz-animation-delay: 12s;
|
||||
-o-animation-delay: 12s;
|
||||
animation-delay: 12s;
|
||||
}
|
||||
#iosDevice img:nth-of-type(2) {
|
||||
-webkit-animation-delay: 8s;
|
||||
-moz-animation-delay: 8s;
|
||||
-o-animation-delay: 8s;
|
||||
animation-delay: 8s;
|
||||
}
|
||||
#iosDevice img:nth-of-type(3) {
|
||||
-webkit-animation-delay: 4s;
|
||||
-moz-animation-delay: 4s;
|
||||
-o-animation-delay: 4s;
|
||||
animation-delay: 4s;
|
||||
}
|
||||
#iosDevice img:nth-of-type(4) {
|
||||
-webkit-animation-delay: 0;
|
||||
-moz-animation-delay: 0;
|
||||
-o-animation-delay: 0;
|
||||
animation-delay: 0;
|
||||
}
|
593
resources/assets/sass/landing/devices.scss
vendored
Normal file
593
resources/assets/sass/landing/devices.scss
vendored
Normal file
|
@ -0,0 +1,593 @@
|
|||
.marvel-device {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
-webkit-box-sizing: content-box !important;
|
||||
box-sizing: content-box !important
|
||||
}
|
||||
|
||||
.marvel-device .screen {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
z-index: 3;
|
||||
background: white;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
border-radius: 1px;
|
||||
-webkit-box-shadow: 0 0 0 3px #111;
|
||||
box-shadow: 0 0 0 3px #111
|
||||
}
|
||||
|
||||
.marvel-device .top-bar,
|
||||
.marvel-device .bottom-bar {
|
||||
height: 3px;
|
||||
background: black;
|
||||
width: 100%;
|
||||
display: block
|
||||
}
|
||||
|
||||
.marvel-device .middle-bar {
|
||||
width: 3px;
|
||||
height: 4px;
|
||||
top: 0px;
|
||||
left: 90px;
|
||||
background: black;
|
||||
position: absolute
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x {
|
||||
width: 375px;
|
||||
height: 812px;
|
||||
padding: 26px;
|
||||
background: #fdfdfd;
|
||||
-webkit-box-shadow: inset 0 0 11px 0 black;
|
||||
box-shadow: inset 0 0 11px 0 black;
|
||||
border-radius: 66px
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x .overflow {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-radius: 66px;
|
||||
overflow: hidden
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x .shadow {
|
||||
border-radius: 100%;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
position: absolute;
|
||||
background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.6) 0%, rgba(255, 255, 255, 0) 60%)
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x .shadow--tl {
|
||||
top: -20px;
|
||||
left: -20px
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x .shadow--tr {
|
||||
top: -20px;
|
||||
right: -20px
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x .shadow--bl {
|
||||
bottom: -20px;
|
||||
left: -20px
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x .shadow--br {
|
||||
bottom: -20px;
|
||||
right: -20px
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x:before {
|
||||
width: calc(100% - 10px);
|
||||
height: calc(100% - 10px);
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
content: '';
|
||||
left: 5px;
|
||||
border-radius: 61px;
|
||||
background: black;
|
||||
z-index: 1
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x .inner-shadow {
|
||||
width: calc(100% - 20px);
|
||||
height: calc(100% - 20px);
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
overflow: hidden;
|
||||
left: 10px;
|
||||
border-radius: 56px;
|
||||
-webkit-box-shadow: inset 0 0 15px 0 rgba(255, 255, 255, 0.66);
|
||||
box-shadow: inset 0 0 15px 0 rgba(255, 255, 255, 0.66);
|
||||
z-index: 1
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x .inner-shadow:before {
|
||||
-webkit-box-shadow: inset 0 0 20px 0 #FFFFFF;
|
||||
box-shadow: inset 0 0 20px 0 #FFFFFF;
|
||||
width: 100%;
|
||||
height: 116%;
|
||||
position: absolute;
|
||||
top: -8%;
|
||||
content: '';
|
||||
left: 0;
|
||||
border-radius: 200px / 112px;
|
||||
z-index: 2
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x .screen {
|
||||
border-radius: 40px;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x .top-bar,
|
||||
.marvel-device.iphone-x .bottom-bar {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
height: 8px;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
left: 0
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x .top-bar {
|
||||
top: 80px
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x .bottom-bar {
|
||||
bottom: 80px
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x .volume,
|
||||
.marvel-device.iphone-x .volume:before,
|
||||
.marvel-device.iphone-x .volume:after,
|
||||
.marvel-device.iphone-x .sleep {
|
||||
width: 3px;
|
||||
background: #b5b5b5;
|
||||
position: absolute
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x .volume {
|
||||
left: -3px;
|
||||
top: 116px;
|
||||
height: 32px
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x .volume:before {
|
||||
height: 62px;
|
||||
top: 62px;
|
||||
content: '';
|
||||
left: 0
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x .volume:after {
|
||||
height: 62px;
|
||||
top: 140px;
|
||||
content: '';
|
||||
left: 0
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x .sleep {
|
||||
height: 96px;
|
||||
top: 200px;
|
||||
right: -3px
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x .camera {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
top: 9px;
|
||||
border-radius: 100%;
|
||||
position: absolute;
|
||||
left: 154px;
|
||||
background: #0d4d71
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x .speaker {
|
||||
height: 6px;
|
||||
width: 60px;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
top: 9px;
|
||||
margin-left: -30px;
|
||||
background: #171818;
|
||||
border-radius: 6px
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x .notch {
|
||||
position: absolute;
|
||||
width: 210px;
|
||||
height: 30px;
|
||||
top: 26px;
|
||||
left: 108px;
|
||||
z-index: 4;
|
||||
background: black;
|
||||
border-bottom-left-radius: 24px;
|
||||
border-bottom-right-radius: 24px
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x .notch:before,
|
||||
.marvel-device.iphone-x .notch:after {
|
||||
content: '';
|
||||
height: 8px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 8px
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x .notch:after {
|
||||
background: radial-gradient(circle at bottom left, transparent 0, transparent 70%, black 70%, black 100%);
|
||||
left: -8px
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x .notch:before {
|
||||
background: radial-gradient(circle at bottom right, transparent 0, transparent 70%, black 70%, black 100%);
|
||||
right: -8px
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x.landscape {
|
||||
height: 375px;
|
||||
width: 812px
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x.landscape .top-bar,
|
||||
.marvel-device.iphone-x.landscape .bottom-bar {
|
||||
width: 8px;
|
||||
height: 100%;
|
||||
top: 0
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x.landscape .top-bar {
|
||||
left: 80px
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x.landscape .bottom-bar {
|
||||
right: 80px;
|
||||
bottom: auto;
|
||||
left: auto
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x.landscape .volume,
|
||||
.marvel-device.iphone-x.landscape .volume:before,
|
||||
.marvel-device.iphone-x.landscape .volume:after,
|
||||
.marvel-device.iphone-x.landscape .sleep {
|
||||
height: 3px
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x.landscape .inner-shadow:before {
|
||||
height: 100%;
|
||||
width: 116%;
|
||||
left: -8%;
|
||||
top: 0;
|
||||
border-radius: 112px / 200px
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x.landscape .volume {
|
||||
bottom: -3px;
|
||||
top: auto;
|
||||
left: 116px;
|
||||
width: 32px
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x.landscape .volume:before {
|
||||
width: 62px;
|
||||
left: 62px;
|
||||
top: 0
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x.landscape .volume:after {
|
||||
width: 62px;
|
||||
left: 140px;
|
||||
top: 0
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x.landscape .sleep {
|
||||
width: 96px;
|
||||
left: 200px;
|
||||
top: -3px;
|
||||
right: auto
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x.landscape .camera {
|
||||
left: 9px;
|
||||
bottom: 154px;
|
||||
top: auto
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x.landscape .speaker {
|
||||
width: 6px;
|
||||
height: 60px;
|
||||
left: 9px;
|
||||
top: 50%;
|
||||
margin-top: -30px;
|
||||
margin-left: 0
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x.landscape .notch {
|
||||
height: 210px;
|
||||
width: 30px;
|
||||
left: 26px;
|
||||
bottom: 108px;
|
||||
top: auto;
|
||||
border-top-right-radius: 24px;
|
||||
border-bottom-right-radius: 24px;
|
||||
border-bottom-left-radius: 0
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x.landscape .notch:before,
|
||||
.marvel-device.iphone-x.landscape .notch:after {
|
||||
left: 0
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x.landscape .notch:after {
|
||||
background: radial-gradient(circle at bottom right, transparent 0, transparent 70%, black 70%, black 100%);
|
||||
bottom: -8px;
|
||||
top: auto
|
||||
}
|
||||
|
||||
.marvel-device.iphone-x.landscape .notch:before {
|
||||
background: radial-gradient(circle at top right, transparent 0, transparent 70%, black 70%, black 100%);
|
||||
top: -8px
|
||||
}
|
||||
|
||||
.marvel-device.note8 {
|
||||
width: 400px;
|
||||
height: 822px;
|
||||
background: black;
|
||||
border-radius: 34px;
|
||||
padding: 45px 10px
|
||||
}
|
||||
|
||||
.marvel-device.note8 .overflow {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-radius: 34px;
|
||||
overflow: hidden
|
||||
}
|
||||
|
||||
.marvel-device.note8 .speaker {
|
||||
height: 8px;
|
||||
width: 56px;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
top: 25px;
|
||||
margin-left: -28px;
|
||||
background: #171818;
|
||||
z-index: 1;
|
||||
border-radius: 8px
|
||||
}
|
||||
|
||||
.marvel-device.note8 .camera {
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
left: 86px;
|
||||
position: absolute;
|
||||
top: 18px;
|
||||
background: #212b36;
|
||||
z-index: 1;
|
||||
border-radius: 100%
|
||||
}
|
||||
|
||||
.marvel-device.note8 .camera:before {
|
||||
content: '';
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
left: -22px;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
background: #212b36;
|
||||
z-index: 1;
|
||||
border-radius: 100%
|
||||
}
|
||||
|
||||
.marvel-device.note8 .sensors {
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
left: 120px;
|
||||
position: absolute;
|
||||
top: 22px;
|
||||
background: #1d233b;
|
||||
z-index: 1;
|
||||
border-radius: 100%
|
||||
}
|
||||
|
||||
.marvel-device.note8 .sensors:before {
|
||||
content: '';
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
left: 18px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
background: #1d233b;
|
||||
z-index: 1;
|
||||
border-radius: 100%
|
||||
}
|
||||
|
||||
.marvel-device.note8 .more-sensors {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
left: 285px;
|
||||
position: absolute;
|
||||
top: 18px;
|
||||
background: #33244a;
|
||||
-webkit-box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.1);
|
||||
box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.1);
|
||||
z-index: 1;
|
||||
border-radius: 100%
|
||||
}
|
||||
|
||||
.marvel-device.note8 .more-sensors:before {
|
||||
content: '';
|
||||
height: 11px;
|
||||
width: 11px;
|
||||
left: 40px;
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
background: #214a61;
|
||||
z-index: 1;
|
||||
border-radius: 100%
|
||||
}
|
||||
|
||||
.marvel-device.note8 .sleep {
|
||||
width: 2px;
|
||||
height: 56px;
|
||||
background: black;
|
||||
position: absolute;
|
||||
top: 288px;
|
||||
right: -2px
|
||||
}
|
||||
|
||||
.marvel-device.note8 .volume {
|
||||
width: 2px;
|
||||
height: 120px;
|
||||
background: black;
|
||||
position: absolute;
|
||||
top: 168px;
|
||||
left: -2px
|
||||
}
|
||||
|
||||
.marvel-device.note8 .volume:before {
|
||||
content: '';
|
||||
top: 168px;
|
||||
width: 2px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
background: black;
|
||||
height: 56px
|
||||
}
|
||||
|
||||
.marvel-device.note8 .inner {
|
||||
width: 100%;
|
||||
height: calc(100% - 8px);
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
content: '';
|
||||
left: 0px;
|
||||
border-radius: 34px;
|
||||
border-top: 2px solid #9fa0a2;
|
||||
border-bottom: 2px solid #9fa0a2;
|
||||
background: black;
|
||||
z-index: 1;
|
||||
-webkit-box-shadow: inset 0 0 6px 0 rgba(255, 255, 255, 0.5);
|
||||
box-shadow: inset 0 0 6px 0 rgba(255, 255, 255, 0.5)
|
||||
}
|
||||
|
||||
.marvel-device.note8 .shadow {
|
||||
-webkit-box-shadow: inset 0 0 60px 0 white, inset 0 0 30px 0 rgba(255, 255, 255, 0.5), 0 0 20px 0 white, 0 0 20px 0 rgba(255, 255, 255, 0.5);
|
||||
box-shadow: inset 0 0 60px 0 white, inset 0 0 30px 0 rgba(255, 255, 255, 0.5), 0 0 20px 0 white, 0 0 20px 0 rgba(255, 255, 255, 0.5);
|
||||
height: 101%;
|
||||
position: absolute;
|
||||
top: -0.5%;
|
||||
content: '';
|
||||
width: calc(100% - 20px);
|
||||
left: 10px;
|
||||
border-radius: 38px;
|
||||
z-index: 5;
|
||||
pointer-events: none
|
||||
}
|
||||
|
||||
.marvel-device.note8 .screen {
|
||||
border-radius: 14px;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none
|
||||
}
|
||||
|
||||
.marvel-device.note8.landscape {
|
||||
height: 400px;
|
||||
width: 822px;
|
||||
padding: 10px 45px
|
||||
}
|
||||
|
||||
.marvel-device.note8.landscape .speaker {
|
||||
height: 56px;
|
||||
width: 8px;
|
||||
top: 50%;
|
||||
margin-top: -28px;
|
||||
margin-left: 0;
|
||||
right: 25px;
|
||||
left: auto
|
||||
}
|
||||
|
||||
.marvel-device.note8.landscape .camera {
|
||||
top: 86px;
|
||||
right: 18px;
|
||||
left: auto
|
||||
}
|
||||
|
||||
.marvel-device.note8.landscape .camera:before {
|
||||
top: -22px;
|
||||
left: 5px
|
||||
}
|
||||
|
||||
.marvel-device.note8.landscape .sensors {
|
||||
top: 120px;
|
||||
right: 22px;
|
||||
left: auto
|
||||
}
|
||||
|
||||
.marvel-device.note8.landscape .sensors:before {
|
||||
top: 18px;
|
||||
left: 0
|
||||
}
|
||||
|
||||
.marvel-device.note8.landscape .more-sensors {
|
||||
top: 285px;
|
||||
right: 18px;
|
||||
left: auto
|
||||
}
|
||||
|
||||
.marvel-device.note8.landscape .more-sensors:before {
|
||||
top: 40px;
|
||||
left: 4px
|
||||
}
|
||||
|
||||
.marvel-device.note8.landscape .sleep {
|
||||
bottom: -2px;
|
||||
top: auto;
|
||||
right: 288px;
|
||||
width: 56px;
|
||||
height: 2px
|
||||
}
|
||||
|
||||
.marvel-device.note8.landscape .volume {
|
||||
width: 120px;
|
||||
height: 2px;
|
||||
top: -2px;
|
||||
right: 168px;
|
||||
left: auto
|
||||
}
|
||||
|
||||
.marvel-device.note8.landscape .volume:before {
|
||||
right: 168px;
|
||||
left: auto;
|
||||
top: 0;
|
||||
width: 56px;
|
||||
height: 2px
|
||||
}
|
||||
|
||||
.marvel-device.note8.landscape .inner {
|
||||
height: 100%;
|
||||
width: calc(100% - 8px);
|
||||
left: 2px;
|
||||
top: 0;
|
||||
border-top: 0;
|
||||
border-bottom: 0;
|
||||
border-left: 2px solid #9fa0a2;
|
||||
border-right: 2px solid #9fa0a2
|
||||
}
|
||||
|
||||
.marvel-device.note8.landscape .shadow {
|
||||
width: 101%;
|
||||
height: calc(100% - 20px);
|
||||
left: -0.5%;
|
||||
top: 10px
|
||||
}
|
|
@ -13,7 +13,7 @@
|
|||
<div class="col-12 col-md-3">
|
||||
<div class="card mb-3 border-left-blue">
|
||||
<div class="card-body text-center">
|
||||
<p class="font-weight-ultralight h2 mb-0 text-truncate">{{$sys['pixelfed']}}</p>
|
||||
<p class="font-weight-ultralight h2 mb-0 text-truncate" title="{{$sys['pixelfed']}}" data-toggle="tooltip">{{$sys['pixelfed']}}</p>
|
||||
</div>
|
||||
<div class="card-footer font-weight-bold py-0 text-center bg-white">Pixelfed</div>
|
||||
</div>
|
||||
|
@ -21,7 +21,7 @@
|
|||
<div class="col-12 col-md-3">
|
||||
<div class="card mb-3 border-left-blue">
|
||||
<div class="card-body text-center">
|
||||
<p class="font-weight-ultralight h2 mb-0 text-truncate">{{$sys['database']['version']}}</p>
|
||||
<p class="font-weight-ultralight h2 mb-0 text-truncate" title="{{$sys['database']['version']}}" data-toggle="tooltip">{{$sys['database']['version']}}</p>
|
||||
</div>
|
||||
<div class="card-footer font-weight-bold py-0 text-center bg-white">{{$sys['database']['name']}}</div>
|
||||
</div>
|
||||
|
@ -29,7 +29,7 @@
|
|||
<div class="col-12 col-md-3">
|
||||
<div class="card mb-3 border-left-blue">
|
||||
<div class="card-body text-center">
|
||||
<p class="font-weight-ultralight h2 mb-0 text-truncate">{{$sys['php']}}</p>
|
||||
<p class="font-weight-ultralight h2 mb-0 text-truncate" title="{{$sys['php']}}" data-toggle="tooltip">{{$sys['php']}}</p>
|
||||
</div>
|
||||
<div class="card-footer font-weight-bold py-0 text-center bg-white">PHP</div>
|
||||
</div>
|
||||
|
@ -37,7 +37,7 @@
|
|||
<div class="col-12 col-md-3">
|
||||
<div class="card mb-3 border-left-blue">
|
||||
<div class="card-body text-center">
|
||||
<p class="font-weight-ultralight h2 mb-0 text-truncate">{{$sys['laravel']}}</p>
|
||||
<p class="font-weight-ultralight h2 mb-0 text-truncate" title="{{$sys['laravel']}}" data-toggle="tooltip">{{$sys['laravel']}}</p>
|
||||
</div>
|
||||
<div class="card-footer font-weight-bold py-0 text-center bg-white">Laravel</div>
|
||||
</div>
|
||||
|
|
|
@ -7,9 +7,14 @@
|
|||
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
@auth
|
||||
<ul class="navbar-nav ml-auto d-none d-md-block">
|
||||
<form class="form-inline search-form">
|
||||
<input class="form-control mr-sm-2 search-form-input" placeholder="{{__('navmenu.search')}}" aria-label="search" autocomplete="off">
|
||||
<ul class="navbar-nav mx-auto pr-3">
|
||||
<form class="form-inline search-bar" method="get" action="/i/results">
|
||||
<div class="input-group">
|
||||
<input class="form-control" name="q" placeholder="{{__('navmenu.search')}}" aria-label="search" autocomplete="off">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-outline-primary" type="submit"><i class="fas fa-search"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</ul>
|
||||
@endauth
|
||||
|
|
|
@ -12,10 +12,11 @@
|
|||
@endsection
|
||||
|
||||
@push('meta')<meta property="og:description" content="{{$profile->bio}}">
|
||||
<meta property="og:image" content="{{$profile->avatarUrl()}}">
|
||||
<link href="{{$profile->permalink('.atom')}}" rel="alternate" title="{{$profile->username}} on PixelFed" type="application/atom+xml">
|
||||
@if(false == $settings['crawlable'] || $profile->remote_url)
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
@else <meta property="og:image" content="{{$profile->avatarUrl()}}">
|
||||
<link href="{{$profile->permalink('.atom')}}" rel="alternate" title="{{$profile->username}} on PixelFed" type="application/atom+xml">
|
||||
<link href='{{$profile->permalink()}}' rel='alternate' type='application/activity+json'>
|
||||
@endif
|
||||
@endpush
|
||||
|
||||
|
|
5
resources/views/pxtv/home.blade.php
Normal file
5
resources/views/pxtv/home.blade.php
Normal file
|
@ -0,0 +1,5 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
|
||||
@endsection
|
|
@ -1,10 +1,12 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<search-results></search-results>
|
||||
<search-results query="{{request()->query('q')}}" profile-id="{{Auth::user()->profile->id}}"></search-results>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script type="text/javascript" src="{{mix('js/compose.js')}}"></script>
|
||||
<script type="text/javascript" src="{{mix('js/search.js')}}"></script>
|
||||
<script type="text/javascript">
|
||||
new Vue({
|
||||
el: '#content'
|
||||
|
|
|
@ -22,11 +22,157 @@
|
|||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<link rel="shortcut icon" type="image/png" href="/img/favicon.png?v=2">
|
||||
<link rel="apple-touch-icon" type="image/png" href="/img/favicon.png?v=2">
|
||||
<link href="{{ mix('css/app.css') }}" rel="stylesheet" data-stylesheet="light">
|
||||
<link href="{{ mix('css/landing.css') }}" rel="stylesheet">
|
||||
</head>
|
||||
<body class="">
|
||||
<main id="content">
|
||||
<landing-page></landing-page>
|
||||
<section class="container">
|
||||
<div class="row py-5 mb-5">
|
||||
<div class="col-12 col-md-6 d-none d-md-block">
|
||||
<div class="m-md-4" style="position: absolute; transform: scale(0.66)">
|
||||
<div class="marvel-device note8" style="position: absolute;z-index:10;">
|
||||
<div class="inner"></div>
|
||||
<div class="overflow">
|
||||
<div class="shadow"></div>
|
||||
</div>
|
||||
<div class="speaker"></div>
|
||||
<div class="sensors"></div>
|
||||
<div class="more-sensors"></div>
|
||||
<div class="sleep"></div>
|
||||
<div class="volume"></div>
|
||||
<div class="camera"></div>
|
||||
<div class="screen">
|
||||
<img src="/img/landing/android_1.jpg" class="img-fluid">
|
||||
</div>
|
||||
</div>
|
||||
<div class="marvel-device iphone-x" style="position: absolute;z-index: 20;margin: 99px 0 0 151px;">
|
||||
<div class="notch">
|
||||
<div class="camera"></div>
|
||||
<div class="speaker"></div>
|
||||
</div>
|
||||
<div class="top-bar"></div>
|
||||
<div class="sleep"></div>
|
||||
<div class="bottom-bar"></div>
|
||||
<div class="volume"></div>
|
||||
<div class="overflow">
|
||||
<div class="shadow shadow--tr"></div>
|
||||
<div class="shadow shadow--tl"></div>
|
||||
<div class="shadow shadow--br"></div>
|
||||
<div class="shadow shadow--bl"></div>
|
||||
</div>
|
||||
<div class="inner-shadow"></div>
|
||||
<div class="screen">
|
||||
<div id="iosDevice">
|
||||
<img v-if="!loading" src="/img/landing/ios_4.jpg" class="img-fluid">
|
||||
<img v-if="!loading" src="/img/landing/ios_3.jpg" class="img-fluid">
|
||||
<img v-if="!loading" src="/img/landing/ios_2.jpg" class="img-fluid">
|
||||
<img src="/img/landing/ios_1.jpg" class="img-fluid">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-5 offset-md-1">
|
||||
<div>
|
||||
<div class="card my-4">
|
||||
<div class="card-body px-lg-5">
|
||||
<div class="text-center pt-3">
|
||||
<img src="/img/pixelfed-icon-color.svg">
|
||||
</div>
|
||||
<div class="py-3 text-center">
|
||||
<h3 class="font-weight-bold">Pixelfed</h3>
|
||||
<p class="mb-0 lead">Photo sharing for everyone</p>
|
||||
</div>
|
||||
<div>
|
||||
@if(true === config('pixelfed.open_registration'))
|
||||
<form class="px-1" method="POST" action="{{ route('register') }}">
|
||||
@csrf
|
||||
<div class="form-group row">
|
||||
<div class="col-md-12">
|
||||
<input id="name" type="text" class="form-control{{ $errors->has('name') ? ' is-invalid' : '' }}" name="name" value="{{ old('name') }}" placeholder="{{ __('Name') }}" required autofocus>
|
||||
|
||||
@if ($errors->has('name'))
|
||||
<span class="invalid-feedback">
|
||||
<strong>{{ $errors->first('name') }}</strong>
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-md-12">
|
||||
<input id="username" type="text" class="form-control{{ $errors->has('username') ? ' is-invalid' : '' }}" name="username" value="{{ old('username') }}" placeholder="{{ __('Username') }}" required>
|
||||
|
||||
@if ($errors->has('username'))
|
||||
<span class="invalid-feedback">
|
||||
<strong>{{ $errors->first('username') }}</strong>
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-md-12">
|
||||
<input id="email" type="email" class="form-control{{ $errors->has('email') ? ' is-invalid' : '' }}" name="email" value="{{ old('email') }}" placeholder="{{ __('E-Mail Address') }}" required>
|
||||
|
||||
@if ($errors->has('email'))
|
||||
<span class="invalid-feedback">
|
||||
<strong>{{ $errors->first('email') }}</strong>
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-md-12">
|
||||
<input id="password" type="password" class="form-control{{ $errors->has('password') ? ' is-invalid' : '' }}" name="password" placeholder="{{ __('Password') }}" required>
|
||||
|
||||
@if ($errors->has('password'))
|
||||
<span class="invalid-feedback">
|
||||
<strong>{{ $errors->first('password') }}</strong>
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-md-12">
|
||||
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" placeholder="{{ __('Confirm Password') }}" required>
|
||||
</div>
|
||||
</div>
|
||||
@if(config('pixelfed.recaptcha'))
|
||||
<div class="row my-3">
|
||||
{!! Recaptcha::render() !!}
|
||||
</div>
|
||||
@endif
|
||||
<div class="form-group row">
|
||||
<div class="col-md-12">
|
||||
<button type="submit" class="btn btn-primary btn-block py-0 font-weight-bold">
|
||||
{{ __('Register') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mb-0 font-weight-bold text-lighter small">By signing up, you agree to our <a href="/site/terms" class="text-muted">Terms of Use</a> and <a href="/site/privacy" class="text-muted">Privacy Policy</a>.</p>
|
||||
</form>
|
||||
@else
|
||||
<div style="min-height: 350px" class="d-flex justify-content-center align-items-center">
|
||||
<div class="text-center">
|
||||
<p class="lead">Registrations are closed.</p>
|
||||
<p class="text-lighter small">You can find a list of other instances on <a href="https://the-federation.info/pixelfed" class="text-muted font-weight-bold">the-federation.info/pixelfed</a> or <a href="https://fediverse.network/pixelfed" class="text-muted font-weight-bold">fediverse.network/pixelfed</a></p>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card card-body">
|
||||
<p class="text-center mb-0 font-weight-bold">Have an account? <a href="/login">Log in</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container py-3">
|
||||
|
@ -38,13 +184,9 @@
|
|||
<a href="{{route('site.privacy')}}" class="text-primary pr-3">{{__('site.privacy')}}</a>
|
||||
<a href="{{route('site.platform')}}" class="text-primary pr-3">API</a>
|
||||
<a href="{{route('site.language')}}" class="text-primary pr-3">{{__('site.language')}}</a>
|
||||
<a href="https://pixelfed.org" class="text-muted float-right" rel="noopener" title="version {{config('pixelfed.version')}}" data-toggle="tooltip">Powered by PixelFed</a>
|
||||
<a href="https://pixelfed.org" class="text-muted float-right" rel="noopener" title="version {{config('pixelfed.version')}}" data-toggle="tooltip">Powered by Pixelfed</a>
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
|
||||
<script type="text/javascript" src="{{mix('js/app.js')}}"></script>
|
||||
<script type="text/javascript" src="{{mix('js/landing.js')}}"></script>
|
||||
</html>
|
||||
|
||||
|
|
|
@ -130,6 +130,8 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
|
|||
|
||||
Route::get('media/preview/{profileId}/{mediaId}', 'ApiController@showTempMedia')->name('temp-media');
|
||||
|
||||
Route::get('results', 'SearchController@results');
|
||||
Route::post('visibility', 'StatusController@toggleVisibility');
|
||||
|
||||
Route::group(['prefix' => 'report'], function () {
|
||||
Route::get('/', 'ReportController@showForm')->name('report.form');
|
||||
|
|
|
@ -8,13 +8,6 @@ use Illuminate\Foundation\Testing\WithoutMiddleware;
|
|||
|
||||
class InstalledTest extends TestCase
|
||||
{
|
||||
/** @test */
|
||||
public function landing_page()
|
||||
{
|
||||
$response = $this->get('/');
|
||||
$response->assertStatus(200);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function nodeinfo_api()
|
||||
{
|
||||
|
|
9
webpack.mix.js
vendored
9
webpack.mix.js
vendored
|
@ -28,16 +28,19 @@ mix.js('resources/assets/js/app.js', 'public/js')
|
|||
// Timeline component
|
||||
.js('resources/assets/js/timeline.js', 'public/js')
|
||||
|
||||
// LandingPage component
|
||||
.js('resources/assets/js/landing.js', 'public/js')
|
||||
|
||||
// ComposeModal component
|
||||
.js('resources/assets/js/compose.js', 'public/js')
|
||||
|
||||
// SearchResults component
|
||||
.js('resources/assets/js/search.js', 'public/js')
|
||||
|
||||
.sass('resources/assets/sass/app.scss', 'public/css', {
|
||||
implementation: require('node-sass')
|
||||
})
|
||||
.sass('resources/assets/sass/appdark.scss', 'public/css', {
|
||||
implementation: require('node-sass')
|
||||
})
|
||||
.sass('resources/assets/sass/landing.scss', 'public/css', {
|
||||
implementation: require('node-sass')
|
||||
})
|
||||
.version();
|
||||
|
|
Loading…
Reference in a new issue