mirror of
https://github.com/pixelfed/pixelfed.git
synced 2024-11-26 00:03:16 +00:00
Add Archive Posts
This commit is contained in:
parent
6e45021fc2
commit
e9ef0c887a
7 changed files with 346 additions and 80 deletions
|
@ -15,7 +15,8 @@ use App\{
|
|||
Media,
|
||||
Notification,
|
||||
Profile,
|
||||
Status
|
||||
Status,
|
||||
StatusArchived
|
||||
};
|
||||
use App\Transformer\Api\{
|
||||
AccountTransformer,
|
||||
|
@ -39,6 +40,7 @@ use App\Jobs\VideoPipeline\{
|
|||
use App\Services\NotificationService;
|
||||
use App\Services\MediaPathService;
|
||||
use App\Services\MediaBlocklistService;
|
||||
use App\Services\StatusService;
|
||||
|
||||
class BaseApiController extends Controller
|
||||
{
|
||||
|
@ -286,4 +288,75 @@ class BaseApiController extends Controller
|
|||
|
||||
return response()->json($res, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
|
||||
public function archive(Request $request, $id)
|
||||
{
|
||||
abort_if(!$request->user(), 403);
|
||||
|
||||
$status = Status::whereNull('in_reply_to_id')
|
||||
->whereNull('reblog_of_id')
|
||||
->whereProfileId($request->user()->profile_id)
|
||||
->findOrFail($id);
|
||||
|
||||
if($status->scope === 'archived') {
|
||||
return [200];
|
||||
}
|
||||
|
||||
$archive = new StatusArchived;
|
||||
$archive->status_id = $status->id;
|
||||
$archive->profile_id = $status->profile_id;
|
||||
$archive->original_scope = $status->scope;
|
||||
$archive->save();
|
||||
|
||||
$status->scope = 'archived';
|
||||
$status->visibility = 'draft';
|
||||
$status->save();
|
||||
|
||||
StatusService::del($status->id);
|
||||
|
||||
// invalidate caches
|
||||
|
||||
return [200];
|
||||
}
|
||||
|
||||
public function unarchive(Request $request, $id)
|
||||
{
|
||||
abort_if(!$request->user(), 403);
|
||||
|
||||
$status = Status::whereNull('in_reply_to_id')
|
||||
->whereNull('reblog_of_id')
|
||||
->whereProfileId($request->user()->profile_id)
|
||||
->findOrFail($id);
|
||||
|
||||
if($status->scope !== 'archived') {
|
||||
return [200];
|
||||
}
|
||||
|
||||
$archive = StatusArchived::whereStatusId($status->id)
|
||||
->whereProfileId($status->profile_id)
|
||||
->firstOrFail();
|
||||
|
||||
$status->scope = $archive->original_scope;
|
||||
$status->visibility = $archive->original_scope;
|
||||
$status->save();
|
||||
|
||||
$archive->delete();
|
||||
|
||||
return [200];
|
||||
}
|
||||
|
||||
public function archivedPosts(Request $request)
|
||||
{
|
||||
abort_if(!$request->user(), 403);
|
||||
|
||||
$statuses = Status::whereProfileId($request->user()->profile_id)
|
||||
->whereScope('archived')
|
||||
->orderByDesc('id')
|
||||
->simplePaginate(10);
|
||||
|
||||
$fractal = new Fractal\Manager();
|
||||
$fractal->setSerializer(new ArraySerializer());
|
||||
$resource = new Fractal\Resource\Collection($statuses, new StatusStatelessTransformer());
|
||||
return $fractal->createData($resource)->toArray();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -616,6 +616,8 @@
|
|||
<div v-if="status && user.id != status.account.id && !relationship.blocking && !user.is_admin" class="list-group-item rounded cursor-pointer 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 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 text-danger text-decoration-none" :href="reportUrl()">Report</a>
|
||||
<div v-if="status && user.id == status.account.id && status.visibility != 'archived'" class="list-group-item rounded cursor-pointer text-danger" @click="archivePost(status)">Archive</div>
|
||||
<div v-if="status && user.id == status.account.id && status.visibility == 'archived'" class="list-group-item rounded cursor-pointer text-danger" @click="unarchivePost(status)">Unarchive</div>
|
||||
<div v-if="status && (user.is_admin || user.id == status.account.id)" class="list-group-item rounded cursor-pointer text-danger" @click="deletePost(ctxMenuStatus)">Delete</div>
|
||||
<div class="list-group-item rounded cursor-pointer text-lighter" @click="closeCtxMenu()">Cancel</div>
|
||||
</div>
|
||||
|
@ -1757,6 +1759,29 @@ export default {
|
|||
});
|
||||
},
|
||||
|
||||
archivePost(status) {
|
||||
if(window.confirm('Are you sure you want to archive this post?') == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
axios.post('/api/pixelfed/v2/status/' + status.id + '/archive')
|
||||
.then(res => {
|
||||
this.$refs.ctxModal.hide();
|
||||
window.location.href = '/';
|
||||
});
|
||||
},
|
||||
|
||||
unarchivePost(status) {
|
||||
if(window.confirm('Are you sure you want to unarchive this post?') == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
axios.post('/api/pixelfed/v2/status/' + status.id + '/unarchive')
|
||||
.then(res => {
|
||||
this.$refs.ctxModal.hide();
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -181,12 +181,16 @@
|
|||
<li v-if="owner" class="nav-item border-top">
|
||||
<a :class="this.mode == 'bookmarks' ? 'nav-link text-dark' : 'nav-link'" href="#" v-on:click.prevent="switchMode('bookmarks')"><i class="fas fa-bookmark"></i> <span class="d-none d-md-inline-block small pl-1">SAVED</span></a>
|
||||
</li>
|
||||
<li v-if="owner" class="nav-item border-top">
|
||||
<a :class="this.mode == 'archives' ? 'nav-link text-dark' : 'nav-link'" href="#" v-on:click.prevent="switchMode('archives')"><i class="far fa-folder-open"></i> <span class="d-none d-md-inline-block small pl-1">ARCHIVES</span></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="container px-0">
|
||||
<div class="profile-timeline mt-md-4">
|
||||
<div class="row" v-if="mode == 'grid'">
|
||||
<div v-if="mode == 'grid'">
|
||||
<div class="row">
|
||||
<div class="col-4 p-1 p-md-3" v-for="(s, index) in timeline" :key="'tlob:'+index">
|
||||
<a class="card info-overlay card-md-border-0" :href="statusUrl(s)" v-once>
|
||||
<div class="square">
|
||||
|
@ -205,7 +209,6 @@
|
|||
/>
|
||||
</div>
|
||||
<div v-else class="square-content">
|
||||
|
||||
<blur-hash-image
|
||||
width="32"
|
||||
height="32"
|
||||
|
@ -234,12 +237,13 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="timeline.length && mode == 'grid'">
|
||||
<div v-if="timeline.length">
|
||||
<infinite-loading @infinite="infiniteTimeline">
|
||||
<div slot="no-more"></div>
|
||||
<div slot="no-results"></div>
|
||||
</infinite-loading>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="mode == 'bookmarks'">
|
||||
<div v-if="bookmarksLoading">
|
||||
<div class="row">
|
||||
|
@ -280,8 +284,9 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="mode == 'collections'">
|
||||
<div v-if="collections.length" class="row">
|
||||
<div v-if="collections.length && collectionsLoaded" class="row">
|
||||
<div class="col-4 p-1 p-sm-2 p-md-3" v-for="(c, index) in collections">
|
||||
<a class="card info-overlay card-md-border-0" :href="c.url">
|
||||
<div class="square">
|
||||
|
@ -298,6 +303,28 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="mode == 'archives'">
|
||||
<div v-if="archives.length" class="col-12 col-md-8 offset-md-2 px-0 mb-sm-3 timeline mt-5">
|
||||
<div class="alert alert-info">
|
||||
<p class="mb-0">Posts you archive can only be seen by you.</p>
|
||||
<p class="mb-0">For more information see the <a href="/site/kb/sharing-media">Sharing Media</a> help center page.</p>
|
||||
</div>
|
||||
|
||||
<div v-for="(status, index) in archives">
|
||||
<status-card
|
||||
:class="{ 'border-top': index === 0 }"
|
||||
:status="status"
|
||||
:reaction-bar="false"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<infinite-loading @infinite="archivesInfiniteLoader">
|
||||
<div slot="no-more"></div>
|
||||
<div slot="no-results"></div>
|
||||
</infinite-loading>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -663,6 +690,7 @@
|
|||
</style>
|
||||
<script type="text/javascript">
|
||||
import VueMasonry from 'vue-masonry-css'
|
||||
import StatusCard from './partials/StatusCard.vue';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
|
@ -671,6 +699,11 @@
|
|||
'profile-settings',
|
||||
'profile-username'
|
||||
],
|
||||
|
||||
components: {
|
||||
StatusCard,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
ids: [],
|
||||
|
@ -684,7 +717,7 @@
|
|||
owner: false,
|
||||
layout: this.profileLayout,
|
||||
mode: 'grid',
|
||||
modes: ['grid', 'collections', 'bookmarks'],
|
||||
modes: ['grid', 'collections', 'bookmarks', 'archives'],
|
||||
modalStatus: false,
|
||||
relationship: {},
|
||||
followers: [],
|
||||
|
@ -700,6 +733,7 @@
|
|||
bookmarks: [],
|
||||
bookmarksPage: 2,
|
||||
collections: [],
|
||||
collectionsLoaded: false,
|
||||
collectionsPage: 2,
|
||||
isMobile: false,
|
||||
ctxEmbedPayload: null,
|
||||
|
@ -709,6 +743,8 @@
|
|||
followingModalSearchCache: null,
|
||||
followingModalTab: 'following',
|
||||
bookmarksLoading: true,
|
||||
archives: [],
|
||||
archivesPage: 2
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
|
@ -734,6 +770,39 @@
|
|||
}
|
||||
}
|
||||
|
||||
if(u.has('m') && this.modes.includes(u.get('m'))) {
|
||||
this.mode = u.get('m');
|
||||
|
||||
if(this.mode == 'bookmarks') {
|
||||
axios.get('/api/local/bookmarks')
|
||||
.then(res => {
|
||||
this.bookmarks = res.data;
|
||||
this.bookmarksLoading = false;
|
||||
}).catch(err => {
|
||||
this.mode = 'grid';
|
||||
});
|
||||
}
|
||||
|
||||
if(this.mode == 'collections') {
|
||||
axios.get('/api/local/profile/collections/' + this.profileId)
|
||||
.then(res => {
|
||||
this.collections = res.data
|
||||
this.collectionsLoaded = true;
|
||||
}).catch(err => {
|
||||
this.mode = 'grid';
|
||||
});
|
||||
}
|
||||
|
||||
if(this.mode == 'archives') {
|
||||
axios.get('/api/pixelfed/v2/statuses/archives')
|
||||
.then(res => {
|
||||
this.archives = res.data;
|
||||
}).catch(err => {
|
||||
this.mode = 'grid';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
mounted() {
|
||||
|
@ -858,19 +927,15 @@
|
|||
},
|
||||
|
||||
switchMode(mode) {
|
||||
this.mode = _.indexOf(this.modes, mode) ? mode : 'grid';
|
||||
if(this.mode == 'bookmarks' && this.bookmarks.length == 0) {
|
||||
axios.get('/api/local/bookmarks')
|
||||
.then(res => {
|
||||
this.bookmarks = res.data;
|
||||
this.bookmarksLoading = false;
|
||||
});
|
||||
}
|
||||
if(this.mode == 'collections' && this.collections.length == 0) {
|
||||
axios.get('/api/local/profile/collections/' + this.profileId)
|
||||
.then(res => {
|
||||
this.collections = res.data
|
||||
});
|
||||
if(mode == 'grid') {
|
||||
this.mode = mode;
|
||||
} else if(mode == 'bookmarks' && this.bookmarks.length) {
|
||||
this.mode = 'bookmarks';
|
||||
} else if(mode == 'collections' && this.collections.length) {
|
||||
this.mode = 'collections';
|
||||
} else {
|
||||
window.location.href = '/' + this.profileUsername + '?m=' + mode;
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1362,6 +1427,23 @@
|
|||
joinedAtFormat(created) {
|
||||
let d = new Date(created);
|
||||
return d.toDateString();
|
||||
},
|
||||
|
||||
archivesInfiniteLoader($state) {
|
||||
axios.get('/api/pixelfed/v2/statuses/archives', {
|
||||
params: {
|
||||
page: this.archivesPage
|
||||
}
|
||||
}).then(res => {
|
||||
if(res.data.length) {
|
||||
this.archives.push(...res.data);
|
||||
this.archivesPage++;
|
||||
$state.loaded();
|
||||
} else {
|
||||
$state.complete();
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,14 +11,16 @@
|
|||
<div class="list-group text-center">
|
||||
<!-- <div v-if="status && status.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="status && status.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()">View Post</div>
|
||||
<div class="list-group-item rounded cursor-pointer" @click="ctxMenuGoToProfile()">View Profile</div>
|
||||
<div v-if="status.visibility !== 'archived'" class="list-group-item rounded cursor-pointer" @click="ctxMenuGoToPost()">View Post</div>
|
||||
<div v-if="status.visibility !== 'archived'" class="list-group-item rounded cursor-pointer" @click="ctxMenuGoToProfile()">View Profile</div>
|
||||
<!-- <div v-if="status && status.local == true && !status.in_reply_to_id" class="list-group-item rounded cursor-pointer" @click="ctxMenuEmbed()">Embed</div>
|
||||
<div class="list-group-item rounded cursor-pointer" @click="ctxMenuCopyLink()">Copy Link</div> -->
|
||||
<div class="list-group-item rounded cursor-pointer" @click="ctxMenuShare()">Share</div>
|
||||
<div v-if="status && profile && profile.is_admin == true" class="list-group-item rounded cursor-pointer" @click="ctxModMenuShow()">Moderation Tools</div>
|
||||
<div v-if="status.visibility !== 'archived'" class="list-group-item rounded cursor-pointer" @click="ctxMenuShare()">Share</div>
|
||||
<div v-if="status && profile && profile.is_admin == true && status.visibility !== 'archived'" class="list-group-item rounded cursor-pointer" @click="ctxModMenuShow()">Moderation Tools</div>
|
||||
<div v-if="status && status.account.id != profile.id" class="list-group-item rounded cursor-pointer text-danger" @click="ctxMenuReportPost()">Report</div>
|
||||
<div v-if="status && (profile.is_admin || profile.id == status.account.id)" class="list-group-item rounded cursor-pointer text-danger" @click="deletePost(status)">Delete</div>
|
||||
<div v-if="status && profile.id == status.account.id && status.visibility !== 'archived'" class="list-group-item rounded cursor-pointer text-danger" @click="archivePost(status)">Archive</div>
|
||||
<div v-if="status && profile.id == status.account.id && status.visibility == 'archived'" class="list-group-item rounded cursor-pointer text-danger" @click="unarchivePost(status)">Unarchive</div>
|
||||
<div v-if="status && (profile.is_admin || profile.id == status.account.id) && status.visibility !== 'archived'" class="list-group-item rounded cursor-pointer text-danger" @click="deletePost(status)">Delete</div>
|
||||
<div class="list-group-item rounded cursor-pointer text-lighter" @click="closeCtxMenu()">Cancel</div>
|
||||
</div>
|
||||
</b-modal>
|
||||
|
@ -680,6 +682,29 @@
|
|||
ownerOrAdmin(status) {
|
||||
return this.owner(status) || this.admin();
|
||||
},
|
||||
|
||||
archivePost(status) {
|
||||
if(window.confirm('Are you sure you want to archive this post?') == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
axios.post('/api/pixelfed/v2/status/' + status.id + '/archive')
|
||||
.then(res => {
|
||||
this.$emit('status-delete', status.id);
|
||||
this.closeModals();
|
||||
});
|
||||
},
|
||||
|
||||
unarchivePost(status) {
|
||||
if(window.confirm('Are you sure you want to unarchive this post?') == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
axios.post('/api/pixelfed/v2/status/' + status.id + '/unarchive')
|
||||
.then(res => {
|
||||
this.closeModals();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -77,7 +77,10 @@
|
|||
<div class="postPresenterContainer" style="background: #000;">
|
||||
|
||||
<div v-if="status.pf_type === 'photo'" class="w-100">
|
||||
<photo-presenter :status="status" v-on:lightbox="lightbox" v-on:togglecw="status.sensitive = false"></photo-presenter>
|
||||
<photo-presenter
|
||||
:status="status"
|
||||
v-on:lightbox="lightbox"
|
||||
v-on:togglecw="status.sensitive = false"/>
|
||||
</div>
|
||||
|
||||
<div v-else-if="status.pf_type === 'video'" class="w-100">
|
||||
|
@ -149,9 +152,13 @@
|
|||
</div>
|
||||
<div class="timestamp mt-2">
|
||||
<p class="small mb-0">
|
||||
<a :href="statusUrl(status)" class="text-muted text-uppercase">
|
||||
<a v-if="status.visibility != 'archived'" :href="statusUrl(status)" class="text-muted text-uppercase">
|
||||
<timeago :datetime="status.created_at" :auto-update="60" :converter-options="{includeSeconds:true}" :title="timestampFormat(status.created_at)" v-b-tooltip.hover.bottom></timeago>
|
||||
</a>
|
||||
<span v-else class="text-muted text-uppercase">
|
||||
Posted <timeago :datetime="status.created_at" :auto-update="60" :converter-options="{includeSeconds:true}" :title="timestampFormat(status.created_at)" v-b-tooltip.hover.bottom></timeago>
|
||||
</span>
|
||||
|
||||
<span v-if="recommended">
|
||||
<span class="px-1">·</span>
|
||||
<span class="text-muted">Based on popular and trending content</span>
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
<div>
|
||||
You can upload the following media types:
|
||||
<ul>
|
||||
@foreach(explode(',', config('pixelfed.media_types')) as $type)
|
||||
@foreach(explode(',', config_cache('pixelfed.media_types')) as $type)
|
||||
<li class="font-weight-bold">{{$type}}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
|
@ -171,4 +171,55 @@
|
|||
</div>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<a class="text-dark font-weight-bold" data-toggle="collapse" href="#collapse12" role="button" aria-expanded="false" aria-controls="collapse11">
|
||||
<i class="fas fa-chevron-down mr-2"></i>
|
||||
What does archive mean?
|
||||
</a>
|
||||
<div class="collapse" id="collapse12">
|
||||
<div>
|
||||
You can archive your posts which prevents anyone from interacting or viewing it.
|
||||
<br />
|
||||
<strong class="text-danger">Archived posts cannot be deleted or otherwise interacted with. You may not recieve interactions (comments, likes, shares) from other servers while a post is archived.</strong>
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<a class="text-dark font-weight-bold" data-toggle="collapse" href="#collapse13" role="button" aria-expanded="false" aria-controls="collapse11">
|
||||
<i class="fas fa-chevron-down mr-2"></i>
|
||||
How can I archive my posts?
|
||||
</a>
|
||||
<div class="collapse" id="collapse13">
|
||||
<div>
|
||||
To archive your posts:
|
||||
<ul>
|
||||
<li>Navigate to the post</li>
|
||||
<li>Open the menu, click the <i class="fas fa-ellipsis-v text-muted mx-2 cursor-pointer"></i> or <i class="fas fa-ellipsis-h text-muted mx-2 cursor-pointer"></i> button</li>
|
||||
<li>Click on <span class="small font-weight-bold cursor-pointer">Archive</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<a class="text-dark font-weight-bold" data-toggle="collapse" href="#collapse14" role="button" aria-expanded="false" aria-controls="collapse11">
|
||||
<i class="fas fa-chevron-down mr-2"></i>
|
||||
How do I unarchive my posts?
|
||||
</a>
|
||||
<div class="collapse" id="collapse14">
|
||||
<div>
|
||||
To unarchive your posts:
|
||||
<ul>
|
||||
<li>Navigate to your profile</li>
|
||||
<li>Click on the <strong>ARCHIVES</strong> tab</li>
|
||||
<li>Scroll to the post you want to unarchive</li>
|
||||
<li>Open the menu, click the <i class="fas fa-ellipsis-v text-muted mx-2 cursor-pointer"></i> or <i class="fas fa-ellipsis-h text-muted mx-2 cursor-pointer"></i> button</li>
|
||||
<li>Click on <span class="small font-weight-bold cursor-pointer">Unarchive</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</p>
|
||||
|
||||
@endsection
|
|
@ -200,6 +200,9 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
|
|||
Route::get('discover/posts/places', 'DiscoverController@trendingPlaces');
|
||||
Route::get('seasonal/yir', 'SeasonalController@getData');
|
||||
Route::post('seasonal/yir', 'SeasonalController@store');
|
||||
Route::post('status/{id}/archive', 'ApiController@archive');
|
||||
Route::post('status/{id}/unarchive', 'ApiController@unarchive');
|
||||
Route::get('statuses/archives', 'ApiController@archivedPosts');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in a new issue