mirror of
https://github.com/pixelfed/pixelfed.git
synced 2024-12-27 15:33:17 +00:00
commit
d652de6f1b
15 changed files with 427 additions and 159 deletions
|
@ -135,6 +135,10 @@
|
|||
- Updated NotificationTransformer, add missing types. ([3a428366](https://github.com/pixelfed/pixelfed/commit/3a428366))
|
||||
- Updated StatusService, fix json bug. ([1ea2db74](https://github.com/pixelfed/pixelfed/commit/1ea2db74))
|
||||
- Updated NotificationTransformer, handle tagged deletes. ([881fa865](https://github.com/pixelfed/pixelfed/commit/881fa865))
|
||||
- Updated horizon config, add new default values. ([90c8a721](https://github.com/pixelfed/pixelfed/commit/90c8a721))
|
||||
- Updated ComposeModal, add maxlength attribute to alt text input. Fixes ([#2490](https://github.com/pixelfed/pixelfed/issues/2490)). ([526b5531](https://github.com/pixelfed/pixelfed/commit/526b5531))
|
||||
- Updated PublicApiController, add state endpoint. ([9fc5a80c](https://github.com/pixelfed/pixelfed/commit/9fc5a80c))
|
||||
- Updated PostComponent, add reply modal. ([a10d851f](https://github.com/pixelfed/pixelfed/commit/a10d851f))
|
||||
|
||||
## [v0.10.9 (2020-04-17)](https://github.com/pixelfed/pixelfed/compare/v0.10.8...v0.10.9)
|
||||
### Added
|
||||
|
|
|
@ -92,32 +92,47 @@ class PublicApiController extends Controller
|
|||
$item = new Fractal\Resource\Item($status, new StatusStatelessTransformer());
|
||||
$res = [
|
||||
'status' => $this->fractal->createData($item)->toArray(),
|
||||
'user' => [],
|
||||
'likes' => [],
|
||||
'shares' => [],
|
||||
'reactions' => [
|
||||
'liked' => false,
|
||||
'shared' => false,
|
||||
'bookmarked' => false,
|
||||
],
|
||||
];
|
||||
return response()->json($res, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
||||
return $res;
|
||||
});
|
||||
return $res;
|
||||
return response()->json($res);
|
||||
}
|
||||
$item = new Fractal\Resource\Item($status, new StatusTransformer());
|
||||
$item = new Fractal\Resource\Item($status, new StatusStatelessTransformer());
|
||||
$res = [
|
||||
'status' => $this->fractal->createData($item)->toArray(),
|
||||
'user' => $this->getUserData($request->user()),
|
||||
'likes' => $this->getLikes($status),
|
||||
'shares' => $this->getShares($status),
|
||||
];
|
||||
return response()->json($res);
|
||||
}
|
||||
|
||||
public function statusState(Request $request, $username, int $postid)
|
||||
{
|
||||
$profile = Profile::whereUsername($username)->whereNull('status')->firstOrFail();
|
||||
$status = Status::whereProfileId($profile->id)->findOrFail($postid);
|
||||
$this->scopeCheck($profile, $status);
|
||||
if(!Auth::check()) {
|
||||
$res = [
|
||||
'user' => [],
|
||||
'likes' => [],
|
||||
'shares' => [],
|
||||
'reactions' => [
|
||||
'liked' => false,
|
||||
'shared' => false,
|
||||
'bookmarked' => false,
|
||||
],
|
||||
];
|
||||
return response()->json($res);
|
||||
}
|
||||
$res = [
|
||||
'user' => $this->getUserData($request->user()),
|
||||
'likes' => [],
|
||||
'shares' => [],
|
||||
'reactions' => [
|
||||
'liked' => $status->liked(),
|
||||
'shared' => $status->shared(),
|
||||
'bookmarked' => $status->bookmarked(),
|
||||
'liked' => (bool) $status->liked(),
|
||||
'shared' => (bool) $status->shared(),
|
||||
'bookmarked' => (bool) $status->bookmarked(),
|
||||
],
|
||||
];
|
||||
return response()->json($res, 200, [], JSON_PRETTY_PRINT);
|
||||
return response()->json($res);
|
||||
}
|
||||
|
||||
public function statusComments(Request $request, $username, int $postId)
|
||||
|
|
|
@ -97,7 +97,29 @@ return [
|
|||
|
||||
'trim' => [
|
||||
'recent' => 60,
|
||||
'pending' => 60,
|
||||
'completed' => 60,
|
||||
'recent_failed' => 10080,
|
||||
'failed' => 10080,
|
||||
'monitored' => 10080,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Metrics
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you can configure how many snapshots should be kept to display in
|
||||
| the metrics graph. This will get used in combination with Horizon's
|
||||
| `horizon:snapshot` schedule to define how long to retain metrics.
|
||||
|
|
||||
*/
|
||||
|
||||
'metrics' => [
|
||||
'trim_snapshots' => [
|
||||
'job' => 24,
|
||||
'queue' => 24,
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|
@ -142,21 +164,25 @@ return [
|
|||
'environments' => [
|
||||
'production' => [
|
||||
'supervisor-1' => [
|
||||
'connection' => 'redis',
|
||||
'queue' => ['high', 'default', 'feed'],
|
||||
'balance' => 'auto',
|
||||
'processes' => 20,
|
||||
'tries' => 3,
|
||||
'connection' => 'redis',
|
||||
'queue' => ['high', 'default', 'feed'],
|
||||
'balance' => 'auto',
|
||||
'maxProcesses' => 20,
|
||||
'memory' => 128,
|
||||
'tries' => 3,
|
||||
'nice' => 0,
|
||||
],
|
||||
],
|
||||
|
||||
'local' => [
|
||||
'supervisor-1' => [
|
||||
'connection' => 'redis',
|
||||
'queue' => ['high', 'default', 'feed'],
|
||||
'balance' => 'auto',
|
||||
'processes' => 20,
|
||||
'tries' => 3,
|
||||
'connection' => 'redis',
|
||||
'queue' => ['high', 'default', 'feed'],
|
||||
'balance' => 'auto',
|
||||
'maxProcesses' => 20,
|
||||
'memory' => 128,
|
||||
'tries' => 3,
|
||||
'nice' => 0,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
|
BIN
public/js/compose.js
vendored
BIN
public/js/compose.js
vendored
Binary file not shown.
BIN
public/js/memoryprofile.js
vendored
BIN
public/js/memoryprofile.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/rempos.js
vendored
BIN
public/js/rempos.js
vendored
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.
|
@ -327,6 +327,7 @@
|
|||
</div>
|
||||
<p class="font-weight-bold text-center small text-muted pt-3 mb-0">When you tag someone, they are sent a notification.<br>For more information on tagging, <a href="#" class="text-primary" @click.prevent="showTagHelpCard()">click here</a>.</p>
|
||||
</div>
|
||||
|
||||
<div v-if="page == 'tagPeopleHelp'" class="w-100 h-100 p-3">
|
||||
<p class="mb-0 text-center py-3 px-2 lead">Tagging someone is like mentioning them, with the option to make it private between you.</p>
|
||||
<p class="mb-3 py-3 px-2 font-weight-lighter">
|
||||
|
@ -420,7 +421,7 @@
|
|||
<div class="media">
|
||||
<img :src="m.preview_url" class="mr-3" width="50px" height="50px">
|
||||
<div class="media-body">
|
||||
<textarea class="form-control" v-model="m.alt" placeholder="Add a media description here..."></textarea>
|
||||
<textarea class="form-control" v-model="m.alt" placeholder="Add a media description here..." maxlength="140"></textarea>
|
||||
<p class="help-text small text-right text-muted mb-0">{{m.alt ? m.alt.length : 0}}/140</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -468,7 +469,7 @@
|
|||
<div class="media-body">
|
||||
<div class="form-group">
|
||||
<label class="font-weight-bold text-muted small">Media Description</label>
|
||||
<textarea class="form-control" v-model="media[carouselCursor].alt" placeholder="Add a media description here..."></textarea>
|
||||
<textarea class="form-control" v-model="media[carouselCursor].alt" placeholder="Add a media description here..." maxlength="140"></textarea>
|
||||
<p class="help-text small text-muted mb-0 d-flex justify-content-between">
|
||||
<span>Describe your photo for people with visual impairments.</span>
|
||||
<span>{{media[carouselCursor].alt ? media[carouselCursor].alt.length : 0}}/140</span>
|
||||
|
|
|
@ -35,24 +35,10 @@
|
|||
</div>
|
||||
<div v-if="user != false" class="float-right">
|
||||
<div class="post-actions">
|
||||
<div 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>
|
||||
<div>
|
||||
<button class="btn btn-link text-dark no-caret" title="Post options" @click="ctxMenu()">
|
||||
<span class="fas fa-ellipsis-v text-muted"></span>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
|
||||
<a class="dropdown-item font-weight-bold" @click="copyPostUrl()">Copy Post Url</a>
|
||||
<a class="dropdown-item font-weight-bold" @click="showEmbedPostModal()">Embed</a>
|
||||
<div 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>
|
||||
</div>
|
||||
<div v-if="ownerOrAdmin()">
|
||||
<a class="dropdown-item font-weight-bold" href="#" v-on:click.prevent="toggleCommentVisibility">{{ showComments ? 'Disable' : 'Enable'}} Comments</a>
|
||||
<a v-if="canEdit" 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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -108,30 +94,16 @@
|
|||
</div>
|
||||
<div class="float-right">
|
||||
<div class="post-actions">
|
||||
<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">
|
||||
<a class="dropdown-item font-weight-bold" @click="copyPostUrl()">Copy Post Url</a>
|
||||
<a class="dropdown-item font-weight-bold" @click="showEmbedPostModal()">Embed</a>
|
||||
<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 v-if="ownerOrAdmin()">
|
||||
<a class="dropdown-item font-weight-bold" href="#" v-on:click.prevent="toggleCommentVisibility">{{ showComments ? 'Disable' : 'Enable'}} Comments</a>
|
||||
<a v-if="canEdit" 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>
|
||||
</div>
|
||||
<div v-if="user != false">
|
||||
<button class="btn btn-link text-dark no-caret" title="Post options" @click="ctxMenu()">
|
||||
<span class="fas fa-ellipsis-v text-muted"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex flex-md-column flex-column-reverse h-100" style="overflow-y: auto;">
|
||||
<div class="card-body status-comments pb-5 pt-0">
|
||||
<div class="card-body status-comments pt-0">
|
||||
<div class="status-comment">
|
||||
<div v-if="status.content.length" class="pt-3">
|
||||
<div v-if="showCaption != true">
|
||||
|
@ -227,7 +199,12 @@
|
|||
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body flex-grow-0 py-1">
|
||||
<div v-if="reactionBarLoading" class="card-body flex-grow-0 py-4 text-center">
|
||||
<div class="spinner-border" role="status">
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="card-body flex-grow-0 py-1">
|
||||
<div v-if="loaded && user.hasOwnProperty('id')" class="reactions my-2 pb-1 d-flex justify-content-between">
|
||||
<h3 v-bind:class="[reactions.liked ? 'fas fa-heart text-danger mr-3 m-0 cursor-pointer' : 'far fa-heart pr-3 m-0 like-btn cursor-pointer']" title="Like" v-on:click="likeStatus"></h3>
|
||||
<h3 v-if="!status.comments_disabled" class="far fa-comment mr-3 m-0 cursor-pointer" title="Comment" v-on:click="replyFocus(status)"></h3>
|
||||
|
@ -252,18 +229,13 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <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" v-for="e in emoji">{{e}}</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 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" :disabled="replyText.length == 0" />
|
||||
<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;" @click="replyFocus(status)"></textarea>
|
||||
<input type="button" value="Post" class="d-inline-block btn btn-link font-weight-bold reply-btn text-decoration-none" disabled/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -271,9 +243,6 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="container" v-if="showProfileMorePosts">
|
||||
<!-- <div class="py-4">
|
||||
<hr>
|
||||
</div> -->
|
||||
<p class="text-lighter px-3 mt-5" style="font-weight: 600;font-size: 15px;">More posts from <a :href="'/'+statusUsername" class="text-dark">{{this.statusUsername}}</a></p>
|
||||
<div class="profile-timeline mt-md-4">
|
||||
<div class="row">
|
||||
|
@ -474,6 +443,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<b-modal ref="likesModal"
|
||||
id="l-modal"
|
||||
hide-footer
|
||||
|
@ -512,9 +482,9 @@
|
|||
hide-footer
|
||||
centered
|
||||
title="Shares"
|
||||
body-class="list-group-flush p-0">
|
||||
body-class="list-group-flush py-3 px-0">
|
||||
<div class="list-group">
|
||||
<div class="list-group-item border-0" v-for="(user, index) in shares" :key="'modal_shares_'+index">
|
||||
<div class="list-group-item border-0 py-1" v-for="(user, index) in shares" :key="'modal_shares_'+index">
|
||||
<div class="media">
|
||||
<a :href="user.url">
|
||||
<img class="mr-3 rounded-circle box-shadow" :src="user.avatar" :alt="user.username + '’s avatar'" width="30px">
|
||||
|
@ -615,6 +585,88 @@
|
|||
</div>
|
||||
<p class="mb-0 text-center small text-muted font-weight-bold"><a href="/site/kb/tagging-people">Learn more</a> about Tagging People.</p>
|
||||
</b-modal>
|
||||
<b-modal ref="ctxModal"
|
||||
id="ctx-modal"
|
||||
hide-header
|
||||
hide-footer
|
||||
centered
|
||||
rounded
|
||||
size="sm"
|
||||
body-class="list-group-flush p-0 rounded">
|
||||
<div class="list-group text-center">
|
||||
<div v-if="user && user.id != status.account.id && relationship && relationship.following" class="list-group-item rounded cursor-pointer font-weight-bold text-danger" @click="ctxMenuUnfollow()">Unfollow</div>
|
||||
<div v-if="user && user.id != status.account.id && relationship && !relationship.following" class="list-group-item rounded cursor-pointer font-weight-bold text-primary" @click="ctxMenuFollow()">Follow</div>
|
||||
<div v-if="status && status.local == true" class="list-group-item rounded cursor-pointer" @click="showEmbedPostModal()">Embed</div>
|
||||
<div class="list-group-item rounded cursor-pointer" @click="ctxMenuCopyLink()">Copy Link</div>
|
||||
<div v-if="status && user.id == status.account.id" class="list-group-item rounded cursor-pointer" @click="toggleCommentVisibility">{{ showComments ? 'Disable' : 'Enable'}} Comments</div>
|
||||
<a v-if="status && user.id == status.account.id" class="list-group-item rounded cursor-pointer text-dark text-decoration-none" :href="editUrl()">Edit</a>
|
||||
<div v-if="user && user.is_admin == true" class="list-group-item rounded cursor-pointer" @click="ctxModMenu()">ModTools</div>
|
||||
<div v-if="status && user.id != status.account.id && !relationship.blocking && !user.is_admin" class="list-group-item rounded cursor-pointer font-weight-bold text-danger" @click="blockProfile()">Block</div>
|
||||
<div v-if="status && user.id != status.account.id && relationship.blocking && !user.is_admin" class="list-group-item rounded cursor-pointer font-weight-bold text-danger" @click="unblockProfile()">Unblock</div>
|
||||
<a v-if="user && user.id != status.account.id && !user.is_admin" class="list-group-item rounded cursor-pointer font-weight-bold text-danger text-decoration-none" :href="reportUrl()">Report</a>
|
||||
<div v-if="status && (user.is_admin || user.id == status.account.id)" class="list-group-item rounded cursor-pointer font-weight-bold text-danger" @click="deletePost(ctxMenuStatus)">Delete</div>
|
||||
<div class="list-group-item rounded cursor-pointer text-lighter" @click="closeCtxMenu()">Cancel</div>
|
||||
</div>
|
||||
</b-modal>
|
||||
<b-modal ref="ctxModModal"
|
||||
id="ctx-mod-modal"
|
||||
hide-header
|
||||
hide-footer
|
||||
centered
|
||||
rounded
|
||||
size="sm"
|
||||
body-class="list-group-flush p-0 rounded">
|
||||
<div class="list-group text-center">
|
||||
<div class="list-group-item rounded cursor-pointer" @click="toggleCommentVisibility">{{ showComments ? 'Disable' : 'Enable'}} Comments</div>
|
||||
|
||||
<div class="list-group-item rounded cursor-pointer" @click="moderatePost('unlist')">Unlist from Timelines</div>
|
||||
<div v-if="status.sensitive" class="list-group-item rounded cursor-pointer" @click="moderatePost('remcw')">Remove Content Warning</div>
|
||||
<div v-else class="list-group-item rounded cursor-pointer" @click="moderatePost('addcw')">Add Content Warning</div>
|
||||
<div class="list-group-item rounded cursor-pointer text-lighter" @click="ctxModMenuClose()">Cancel</div>
|
||||
</div>
|
||||
</b-modal>
|
||||
<b-modal ref="replyModal"
|
||||
id="ctx-reply-modal"
|
||||
hide-footer
|
||||
centered
|
||||
rounded
|
||||
:title-html="replyingToUsername ? 'Reply to <span class=text-dark>' + replyingToUsername + '</span>' : ''"
|
||||
title-tag="p"
|
||||
title-class="font-weight-bold text-muted"
|
||||
size="md"
|
||||
body-class="p-2 rounded">
|
||||
<div>
|
||||
<textarea class="form-control" rows="4" style="border: none; font-size: 18px; resize: none; white-space: pre-wrap;outline: none;" placeholder="Reply here ..." v-model="replyText">
|
||||
</textarea>
|
||||
|
||||
<div class="border-top border-bottom my-2">
|
||||
<ul class="nav align-items-center emoji-reactions" style="overflow-x: scroll;flex-wrap: unset;">
|
||||
<li class="nav-item" v-on:click="emojiReaction(status)" v-for="e in emoji">{{e}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<span class="pl-2 small text-muted font-weight-bold text-monospace">
|
||||
<span :class="[replyText.length > config.uploader.max_caption_length ? 'text-danger':'text-dark']">{{replyText.length > config.uploader.max_caption_length ? config.uploader.max_caption_length - replyText.length : replyText.length}}</span>/{{config.uploader.max_caption_length}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="custom-control custom-switch mr-3">
|
||||
<input type="checkbox" class="custom-control-input" id="replyModalCWSwitch" v-model="replySensitive">
|
||||
<label :class="[replySensitive ? 'custom-control-label font-weight-bold text-dark':'custom-control-label text-lighter']" for="replyModalCWSwitch">Mark as NSFW</label>
|
||||
</div>
|
||||
<!-- <select class="custom-select custom-select-sm my-0 mr-2">
|
||||
<option value="public" selected="">Public</option>
|
||||
<option value="unlisted">Unlisted</option>
|
||||
<option value="followers">Followers Only</option>
|
||||
</select> -->
|
||||
<button class="btn btn-primary btn-sm py-2 px-4 lead text-uppercase font-weight-bold" v-on:click.prevent="postReply()" :disabled="replyText.length == 0">
|
||||
{{replySending == true ? 'POSTING' : 'POST'}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</b-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -742,6 +794,7 @@ export default {
|
|||
loaded: false,
|
||||
loading: null,
|
||||
replyingToId: this.statusId,
|
||||
replyingToUsername: this.statusUsername,
|
||||
replyToIndex: 0,
|
||||
replySending: false,
|
||||
emoji: window.App.util.emoji,
|
||||
|
@ -753,9 +806,10 @@ export default {
|
|||
ctxEmbedShowLikes: false,
|
||||
ctxEmbedCompactMode: false,
|
||||
layout: this.profileLayout,
|
||||
canEdit: false,
|
||||
showProfileMorePosts: false,
|
||||
profileMorePosts: []
|
||||
profileMorePosts: [],
|
||||
replySending: false,
|
||||
reactionBarLoading: true,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
@ -811,16 +865,6 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
showMuteBlock() {
|
||||
let sid = this.status.account.id;
|
||||
let uid = this.user.id;
|
||||
if(sid == uid) {
|
||||
$('.post-actions .menu-author').removeClass('d-none');
|
||||
} else {
|
||||
$('.post-actions .menu-user').removeClass('d-none');
|
||||
}
|
||||
},
|
||||
|
||||
reportUrl() {
|
||||
return '/i/report?type=post&id=' + this.status.id;
|
||||
},
|
||||
|
@ -839,33 +883,20 @@ export default {
|
|||
axios.get('/api/v2/profile/'+this.statusUsername+'/status/'+this.statusId)
|
||||
.then(response => {
|
||||
self.status = response.data.status;
|
||||
self.user = response.data.user;
|
||||
window._sharedData.curUser = self.user;
|
||||
window.App.util.navatar();
|
||||
self.media = self.status.media_attachments;
|
||||
self.reactions = response.data.reactions;
|
||||
self.likes = response.data.likes;
|
||||
self.shares = response.data.shares;
|
||||
self.likesPage = 2;
|
||||
self.sharesPage = 2;
|
||||
this.showMuteBlock();
|
||||
self.showCaption = !response.data.status.sensitive;
|
||||
if(self.status.comments_disabled == false) {
|
||||
self.showComments = true;
|
||||
this.fetchComments();
|
||||
}
|
||||
if(this.ownerOrAdmin()) {
|
||||
let od = new Date(this.status.created_at).getTime() + (1 * 24 * 60 * 60 * 1000);
|
||||
let now = new Date().getTime();
|
||||
if(od > now) {
|
||||
this.canEdit = true;
|
||||
}
|
||||
}
|
||||
this.loaded = true;
|
||||
setTimeout(function() {
|
||||
self.fetchProfilePosts();
|
||||
}, 3000);
|
||||
setTimeout(function() {
|
||||
self.fetchState();
|
||||
document.querySelectorAll('.status-comment .comment-text a').forEach(function(i, e) {
|
||||
if(i.href.startsWith(window.location.origin)) {
|
||||
return;
|
||||
|
@ -882,6 +913,20 @@ export default {
|
|||
});
|
||||
},
|
||||
|
||||
fetchState() {
|
||||
let self = this;
|
||||
axios.get('/api/v2/profile/'+this.statusUsername+'/status/'+this.statusId+'/state')
|
||||
.then(res => {
|
||||
self.user = res.data.user;
|
||||
window._sharedData.curUser = self.user;
|
||||
window.App.util.navatar();
|
||||
self.likes = res.data.likes;
|
||||
self.shares = res.data.shares;
|
||||
self.reactions = res.data.reactions;
|
||||
self.reactionBarLoading = false;
|
||||
});
|
||||
},
|
||||
|
||||
likesModal() {
|
||||
if($('body').hasClass('loggedIn') == false) {
|
||||
window.location.href = '/login?next=' + encodeURIComponent('/p/' + this.status.shortcode);
|
||||
|
@ -890,14 +935,31 @@ export default {
|
|||
if(this.status.favourites_count == 0) {
|
||||
return;
|
||||
}
|
||||
this.$refs.likesModal.show();
|
||||
if(this.likes.length) {
|
||||
this.$refs.likesModal.show();
|
||||
return;
|
||||
}
|
||||
axios.get('/api/v2/likes/profile/'+this.statusUsername+'/status/'+this.statusId)
|
||||
.then(res => {
|
||||
this.likes = res.data.data;
|
||||
this.$refs.likesModal.show();
|
||||
});
|
||||
},
|
||||
|
||||
sharesModal() {
|
||||
if(this.status.reblogs_count == 0 || $('body').hasClass('loggedIn') == false) {
|
||||
window.location.href = '/login?next=' + encodeURIComponent('/p/' + this.status.shortcode);
|
||||
return;
|
||||
}
|
||||
this.$refs.sharesModal.show();
|
||||
if(this.shares.length) {
|
||||
this.$refs.sharesModal.show();
|
||||
return;
|
||||
}
|
||||
axios.get('/api/v2/shares/profile/'+this.statusUsername+'/status/'+this.statusId)
|
||||
.then(res => {
|
||||
this.shares = res.data.data;
|
||||
this.$refs.sharesModal.show();
|
||||
});
|
||||
},
|
||||
|
||||
infiniteLikesHandler($state) {
|
||||
|
@ -1010,21 +1072,6 @@ export default {
|
|||
});
|
||||
},
|
||||
|
||||
muteProfile() {
|
||||
if($('body').hasClass('loggedIn') == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
axios.post('/i/mute', {
|
||||
type: 'user',
|
||||
item: this.status.account.id
|
||||
}).then(res => {
|
||||
swal('Success', 'You have successfully muted ' + this.status.account.acct, 'success');
|
||||
}).catch(err => {
|
||||
swal('Error', 'Something went wrong. Please try again later.', 'error');
|
||||
});
|
||||
},
|
||||
|
||||
blockProfile() {
|
||||
if($('body').hasClass('loggedIn') == false) {
|
||||
return;
|
||||
|
@ -1034,12 +1081,31 @@ export default {
|
|||
type: 'user',
|
||||
item: this.status.account.id
|
||||
}).then(res => {
|
||||
this.$refs.ctxModal.hide();
|
||||
this.relationship.blocking = true;
|
||||
swal('Success', 'You have successfully blocked ' + this.status.account.acct, 'success');
|
||||
}).catch(err => {
|
||||
swal('Error', 'Something went wrong. Please try again later.', 'error');
|
||||
});
|
||||
},
|
||||
|
||||
unblockProfile() {
|
||||
if($('body').hasClass('loggedIn') == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
axios.post('/i/unblock', {
|
||||
type: 'user',
|
||||
item: this.status.account.id
|
||||
}).then(res => {
|
||||
this.relationship.blocking = false;
|
||||
this.$refs.ctxModal.hide();
|
||||
swal('Success', 'You have successfully unblocked ' + this.status.account.acct, 'success');
|
||||
}).catch(err => {
|
||||
swal('Error', 'Something went wrong. Please try again later.', 'error');
|
||||
});
|
||||
},
|
||||
|
||||
deletePost(status) {
|
||||
if(!this.ownerOrAdmin()) {
|
||||
return;
|
||||
|
@ -1082,6 +1148,7 @@ export default {
|
|||
|
||||
postReply() {
|
||||
let self = this;
|
||||
this.replySending = true;
|
||||
if(this.replyText.length == 0 ||
|
||||
this.replyText.trim() == '@'+this.status.account.acct) {
|
||||
self.replyText = null;
|
||||
|
@ -1106,7 +1173,7 @@ export default {
|
|||
self.results.unshift(entity);
|
||||
}
|
||||
let elem = $('.status-comments')[0];
|
||||
elem.scrollTop = elem.clientHeight;
|
||||
elem.scrollTop = elem.clientHeight * 2;
|
||||
} else {
|
||||
if(self.replyToIndex >= 0) {
|
||||
let el = self.results[self.replyToIndex];
|
||||
|
@ -1114,6 +1181,8 @@ export default {
|
|||
el.reply_count = el.reply_count + 1;
|
||||
}
|
||||
}
|
||||
self.$refs.replyModal.hide();
|
||||
self.replySending = false;
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -1147,15 +1216,25 @@ export default {
|
|||
},
|
||||
|
||||
replyFocus(e, index, prependUsername = false) {
|
||||
if($('body').hasClass('loggedIn') == false) {
|
||||
this.redirect('/login?next=' + encodeURIComponent(window.location.pathname));
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.status.comments_disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.replyToIndex = index;
|
||||
this.replyingToId = e.id;
|
||||
this.replyingToUsername = e.account.username;
|
||||
this.reply_to_profile_id = e.account.id;
|
||||
let username = e.account.local ? '@' + e.account.username + ' '
|
||||
: '@' + e.account.acct + ' ';
|
||||
if(prependUsername == true) {
|
||||
this.replyText = username;
|
||||
}
|
||||
$('textarea[name="comment"]').focus();
|
||||
this.$refs.replyModal.show();
|
||||
},
|
||||
|
||||
fetchComments() {
|
||||
|
@ -1289,7 +1368,9 @@ export default {
|
|||
item: self.status.id,
|
||||
disableComments: false
|
||||
}).then(function(res) {
|
||||
window.location.href = self.status.url;
|
||||
self.status.comments_disabled = false;
|
||||
self.$refs.ctxModal.hide();
|
||||
window.location.reload();
|
||||
}).catch(function(err) {
|
||||
return;
|
||||
});
|
||||
|
@ -1299,8 +1380,9 @@ export default {
|
|||
item: self.status.id,
|
||||
disableComments: true
|
||||
}).then(function(res) {
|
||||
self.status.comments_disabled = false;
|
||||
self.status.comments_disabled = true;
|
||||
self.showComments = false;
|
||||
self.$refs.ctxModal.hide();
|
||||
}).catch(function(err) {
|
||||
return;
|
||||
});
|
||||
|
@ -1374,6 +1456,7 @@ export default {
|
|||
showEmbedPostModal() {
|
||||
let mode = this.ctxEmbedCompactMode ? 'compact' : 'full';
|
||||
this.ctxEmbedPayload = window.App.util.embed.post(this.status.url, this.ctxEmbedShowCaption, this.ctxEmbedShowLikes, mode);
|
||||
this.$refs.ctxModal.hide();
|
||||
this.$refs.embedModal.show();
|
||||
},
|
||||
|
||||
|
@ -1461,10 +1544,166 @@ export default {
|
|||
swal('An Error Occurred', 'Please try again later.', 'error');
|
||||
});
|
||||
},
|
||||
|
||||
copyPostUrl() {
|
||||
navigator.clipboard.writeText(this.statusUrl);
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
moderatePost(action, $event) {
|
||||
let status = this.status;
|
||||
let username = status.account.username;
|
||||
let msg = '';
|
||||
let self = this;
|
||||
switch(action) {
|
||||
case 'addcw':
|
||||
msg = 'Are you sure you want to add a content warning to this post?';
|
||||
swal({
|
||||
title: 'Confirm',
|
||||
text: msg,
|
||||
icon: 'warning',
|
||||
buttons: true,
|
||||
dangerMode: true
|
||||
}).then(res => {
|
||||
if(res) {
|
||||
axios.post('/api/v2/moderator/action', {
|
||||
action: action,
|
||||
item_id: status.id,
|
||||
item_type: 'status'
|
||||
}).then(res => {
|
||||
swal('Success', 'Successfully added content warning', 'success');
|
||||
status.sensitive = true;
|
||||
self.ctxModMenuClose();
|
||||
}).catch(err => {
|
||||
swal(
|
||||
'Error',
|
||||
'Something went wrong, please try again later.',
|
||||
'error'
|
||||
);
|
||||
self.ctxModMenuClose();
|
||||
});
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'remcw':
|
||||
msg = 'Are you sure you want to remove the content warning on this post?';
|
||||
swal({
|
||||
title: 'Confirm',
|
||||
text: msg,
|
||||
icon: 'warning',
|
||||
buttons: true,
|
||||
dangerMode: true
|
||||
}).then(res => {
|
||||
if(res) {
|
||||
axios.post('/api/v2/moderator/action', {
|
||||
action: action,
|
||||
item_id: status.id,
|
||||
item_type: 'status'
|
||||
}).then(res => {
|
||||
swal('Success', 'Successfully added content warning', 'success');
|
||||
status.sensitive = false;
|
||||
self.ctxModMenuClose();
|
||||
}).catch(err => {
|
||||
swal(
|
||||
'Error',
|
||||
'Something went wrong, please try again later.',
|
||||
'error'
|
||||
);
|
||||
self.ctxModMenuClose();
|
||||
});
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'unlist':
|
||||
msg = 'Are you sure you want to unlist this post?';
|
||||
swal({
|
||||
title: 'Confirm',
|
||||
text: msg,
|
||||
icon: 'warning',
|
||||
buttons: true,
|
||||
dangerMode: true
|
||||
}).then(res => {
|
||||
if(res) {
|
||||
axios.post('/api/v2/moderator/action', {
|
||||
action: action,
|
||||
item_id: status.id,
|
||||
item_type: 'status'
|
||||
}).then(res => {
|
||||
// this.feed = this.feed.filter(f => {
|
||||
// return f.id != status.id;
|
||||
// });
|
||||
swal('Success', 'Successfully unlisted post', 'success');
|
||||
self.ctxModMenuClose();
|
||||
}).catch(err => {
|
||||
self.ctxModMenuClose();
|
||||
swal(
|
||||
'Error',
|
||||
'Something went wrong, please try again later.',
|
||||
'error'
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
ctxMenu() {
|
||||
this.$refs.ctxModal.show();
|
||||
return;
|
||||
},
|
||||
|
||||
closeCtxMenu(truncate) {
|
||||
this.$refs.ctxModal.hide();
|
||||
},
|
||||
|
||||
ctxModMenu() {
|
||||
this.$refs.ctxModal.hide();
|
||||
this.$refs.ctxModModal.show();
|
||||
},
|
||||
|
||||
ctxModMenuClose() {
|
||||
this.$refs.ctxModal.hide();
|
||||
this.$refs.ctxModModal.hide();
|
||||
},
|
||||
|
||||
ctxMenuCopyLink() {
|
||||
let status = this.status;
|
||||
navigator.clipboard.writeText(status.url);
|
||||
this.closeCtxMenu();
|
||||
return;
|
||||
},
|
||||
|
||||
ctxMenuFollow() {
|
||||
let id = this.status.account.id;
|
||||
axios.post('/i/follow', {
|
||||
item: id
|
||||
}).then(res => {
|
||||
let username = this.status.account.acct;
|
||||
this.relationship.following = true;
|
||||
this.$refs.ctxModal.hide();
|
||||
setTimeout(function() {
|
||||
swal('Follow successful!', 'You are now following ' + username, 'success');
|
||||
}, 500);
|
||||
});
|
||||
},
|
||||
|
||||
ctxMenuUnfollow() {
|
||||
let id = this.status.account.id;
|
||||
axios.post('/i/follow', {
|
||||
item: id
|
||||
}).then(res => {
|
||||
let username = this.status.account.acct;
|
||||
this.relationship.following = false;
|
||||
this.$refs.ctxModal.hide();
|
||||
setTimeout(function() {
|
||||
swal('Unfollow successful!', 'You are no longer following ' + username, 'success');
|
||||
}, 500);
|
||||
});
|
||||
},
|
||||
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -57,35 +57,8 @@
|
|||
<!-- a class="list-group-item font-weight-bold text-decoration-none" :href="status.url">Share</a>
|
||||
<a class="list-group-item font-weight-bold text-decoration-none" :href="status.url">Embed</a> -->
|
||||
<a class="list-group-item text-dark text-decoration-none" href="#" @click.prevent="hidePost(status)">Hide</a>
|
||||
<a v-if="activeSession == true && !statusOwner(status)" class="list-group-item text-dark text-decoration-none" :href="reportUrl(status)">Report</a>
|
||||
<a v-if="activeSession == true && !statusOwner(status)" class="list-group-item text-dark text-decoration-none" @click.prevent="muteProfile(status)" href="#">Mute Profile</a>
|
||||
<a v-if="activeSession == true && !statusOwner(status)" class="list-group-item text-dark text-decoration-none" @click.prevent="blockProfile(status)" href="#">Block Profile</a>
|
||||
<span v-if="activeSession == true && statusOwner(status) == true || profile.is_admin == true">
|
||||
<a class="list-group-item text-danger text-decoration-none" @click.prevent="deletePost">Delete</a>
|
||||
</span>
|
||||
<span v-if="activeSession == true && profile.is_admin == true">
|
||||
<a class="list-group-item text-dark text-decoration-none" v-on:click="moderatePost(status, 'autocw')" href="#">
|
||||
<p class="mb-0">Enforce CW</p>
|
||||
<p class="mb-0 small text-muted">Adds a CW to every post <br> made by this account.</p>
|
||||
</a>
|
||||
<a class="list-group-item text-dark text-decoration-none" v-on:click="moderatePost(status, 'noautolink')" href="#">
|
||||
<p class="mb-0">No Autolinking</p>
|
||||
<p class="mb-0 small text-muted">Do not transform mentions, <br> hashtags or urls into HTML.</p>
|
||||
</a>
|
||||
<a class="list-group-item text-dark text-decoration-none" v-on:click="moderatePost(status, 'unlisted')" href="#">
|
||||
<p class="mb-0">Unlisted Posts</p>
|
||||
<p class="mb-0 small text-muted">Removes account from <br> public/network timelines.</p>
|
||||
</a>
|
||||
<a class="list-group-item text-dark text-decoration-none" v-on:click="moderatePost(status, 'disable')" href="#">
|
||||
<p class="mb-0">Disable Account</p>
|
||||
<p class="mb-0 small text-muted">Temporarily disable account <br> until next time user log in.</p>
|
||||
</a>
|
||||
<a class="list-group-item text-dark text-decoration-none" v-on:click="moderatePost(status, 'suspend')" href="#">
|
||||
<p class="mb-0">Suspend Account</p>
|
||||
<p class="mb-0 small text-muted">This prevents any new interactions, <br> without deleting existing data.</p>
|
||||
</a>
|
||||
|
||||
</span>
|
||||
<a v-if="activeSession == true && !statusOwner(status)" class="list-group-item text-danger font-weight-bold text-decoration-none" :href="reportUrl(status)">Report</a>
|
||||
<div v-if="activeSession == true && statusOwner(status) == true || profile.is_admin == true" class="list-group-item text-danger font-weight-bold cursor-pointer" @click.prevent="deletePost">Delete</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -438,11 +438,11 @@
|
|||
size="sm"
|
||||
body-class="list-group-flush p-0 rounded">
|
||||
<div class="list-group text-center">
|
||||
<div v-if="ctxMenuStatus && ctxMenuStatus.account.id != profile.id" class="list-group-item rounded cursor-pointer font-weight-bold text-danger" @click="ctxMenuReportPost()">Report inappropriate</div>
|
||||
<div v-if="ctxMenuStatus && ctxMenuStatus.account.id != profile.id" class="list-group-item rounded cursor-pointer font-weight-bold text-danger" @click="ctxMenuReportPost()">Report</div>
|
||||
<div v-if="ctxMenuStatus && ctxMenuStatus.account.id != profile.id && ctxMenuRelationship && ctxMenuRelationship.following" class="list-group-item rounded cursor-pointer font-weight-bold text-danger" @click="ctxMenuUnfollow()">Unfollow</div>
|
||||
<div v-if="ctxMenuStatus && ctxMenuStatus.account.id != profile.id && ctxMenuRelationship && !ctxMenuRelationship.following" class="list-group-item rounded cursor-pointer font-weight-bold text-primary" @click="ctxMenuFollow()">Follow</div>
|
||||
<div class="list-group-item rounded cursor-pointer" @click="ctxMenuGoToPost()">Go to post</div>
|
||||
<div v-if="ctxMenuStatus && ctxMenuStatus.local == true" class="list-group-item rounded cursor-pointer" @click="ctxMenuEmbed()">Embed</div>
|
||||
<div v-if="ctxMenuStatus && ctxMenuStatus.local == true && !ctxMenuStatus.in_reply_to_id" class="list-group-item rounded cursor-pointer" @click="ctxMenuEmbed()">Embed</div>
|
||||
<!-- <div class="list-group-item rounded cursor-pointer" @click="ctxMenuShare()">Share</div> -->
|
||||
<div class="list-group-item rounded cursor-pointer" @click="ctxMenuCopyLink()">Copy Link</div>
|
||||
<div v-if="profile && profile.is_admin == true" class="list-group-item rounded cursor-pointer" @click="ctxModMenuShow()">Moderation Tools</div>
|
||||
|
@ -558,6 +558,10 @@
|
|||
</span>
|
||||
</div>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="custom-control custom-switch mr-3">
|
||||
<input type="checkbox" class="custom-control-input" id="replyModalCWSwitch" v-model="replyNsfw">
|
||||
<label :class="[replyNsfw ? 'custom-control-label font-weight-bold text-dark':'custom-control-label text-lighter']" for="replyModalCWSwitch">Mark as NSFW</label>
|
||||
</div>
|
||||
<!-- <select class="custom-select custom-select-sm my-0 mr-2">
|
||||
<option value="public" selected="">Public</option>
|
||||
<option value="unlisted">Unlisted</option>
|
||||
|
@ -675,6 +679,7 @@
|
|||
showReadMore: true,
|
||||
replyStatus: {},
|
||||
replyText: '',
|
||||
replyNsfw: false,
|
||||
emoji: window.App.util.emoji,
|
||||
showHashtagPosts: false,
|
||||
hashtagPosts: [],
|
||||
|
@ -697,6 +702,7 @@
|
|||
mpPoller: null
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
ctxEmbedShowCaption: function (n,o) {
|
||||
if(n == true) {
|
||||
|
@ -721,6 +727,7 @@
|
|||
this.ctxEmbedPayload = window.App.util.embed.post(this.ctxMenuStatus.url, this.ctxEmbedShowCaption, this.ctxEmbedShowLikes, mode);
|
||||
}
|
||||
},
|
||||
|
||||
beforeMount() {
|
||||
this.fetchProfile();
|
||||
this.fetchTimelineApi();
|
||||
|
@ -1072,7 +1079,8 @@
|
|||
}
|
||||
axios.post('/i/comment', {
|
||||
item: id,
|
||||
comment: comment
|
||||
comment: comment,
|
||||
sensitive: this.replyNsfw
|
||||
}).then(res => {
|
||||
this.replyText = '';
|
||||
this.replies.unshift(res.data.entity);
|
||||
|
@ -1663,6 +1671,7 @@
|
|||
}, 500);
|
||||
},
|
||||
},
|
||||
|
||||
beforeDestroy () {
|
||||
clearInterval(this.mpInterval);
|
||||
},
|
||||
|
|
|
@ -120,6 +120,7 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
|
|||
Route::get('discover', 'InternalApiController@discover');
|
||||
Route::get('discover/posts', 'InternalApiController@discoverPosts')->middleware('auth:api');
|
||||
Route::get('profile/{username}/status/{postid}', 'PublicApiController@status');
|
||||
Route::get('profile/{username}/status/{postid}/state', 'PublicApiController@statusState');
|
||||
Route::get('comments/{username}/status/{postId}', 'PublicApiController@statusComments');
|
||||
Route::get('likes/profile/{username}/status/{id}', 'PublicApiController@statusLikes');
|
||||
Route::get('shares/profile/{username}/status/{id}', 'PublicApiController@statusShares');
|
||||
|
|
Loading…
Reference in a new issue