mirror of
https://github.com/pixelfed/pixelfed.git
synced 2024-12-26 23:13:17 +00:00
commit
edf3ba0603
11 changed files with 576 additions and 43 deletions
|
@ -68,6 +68,8 @@ class AdminApiController extends Controller
|
|||
|
||||
public function moderateStatus(Request $request)
|
||||
{
|
||||
abort(400, 'Unpublished API');
|
||||
return;
|
||||
$this->validate($request, [
|
||||
'type' => 'required|string|in:status,profile',
|
||||
'id' => 'required|integer|min:1',
|
||||
|
|
|
@ -23,16 +23,11 @@ class FollowerController extends Controller
|
|||
public function store(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'item' => 'required|integer',
|
||||
'item' => 'required|string',
|
||||
]);
|
||||
$item = $request->input('item');
|
||||
$item = (int) $request->input('item');
|
||||
$this->handleFollowRequest($item);
|
||||
if($request->wantsJson()) {
|
||||
return response()->json([
|
||||
200
|
||||
], 200);
|
||||
}
|
||||
return redirect()->back();
|
||||
return response()->json(200);
|
||||
}
|
||||
|
||||
protected function handleFollowRequest($item)
|
||||
|
|
|
@ -95,7 +95,6 @@ class SettingsController extends Controller
|
|||
$user = Auth::user();
|
||||
abort_if(!config('pixelfed.account_deletion'), 403);
|
||||
abort_if($user->is_admin, 403);
|
||||
abort_if($user->created_at->gt(now()->subHours(12)), 403);
|
||||
|
||||
return view('settings.remove.temporary');
|
||||
}
|
||||
|
@ -105,7 +104,6 @@ class SettingsController extends Controller
|
|||
$user = Auth::user();
|
||||
abort_if(!config('pixelfed.account_deletion'), 403);
|
||||
abort_if($user->is_admin, 403);
|
||||
abort_if($user->created_at->gt(now()->subHours(12)), 403);
|
||||
$profile = $user->profile;
|
||||
$user->status = 'disabled';
|
||||
$profile->status = 'disabled';
|
||||
|
@ -120,7 +118,6 @@ class SettingsController extends Controller
|
|||
{
|
||||
$user = Auth::user();
|
||||
abort_if($user->is_admin, 403);
|
||||
abort_if($user->created_at->gt(now()->subDays(7)), 403);
|
||||
return view('settings.remove.permanent');
|
||||
}
|
||||
|
||||
|
@ -132,7 +129,6 @@ class SettingsController extends Controller
|
|||
$user = Auth::user();
|
||||
abort_if(!config('pixelfed.account_deletion'), 403);
|
||||
abort_if($user->is_admin, 403);
|
||||
abort_if($user->created_at->gt(now()->subDays(7)), 403);
|
||||
$profile = $user->profile;
|
||||
$ts = Carbon::now()->addMonth();
|
||||
$user->status = 'delete';
|
||||
|
|
523
resources/assets/js/components/ComposeClassic.vue
Normal file
523
resources/assets/js/components/ComposeClassic.vue
Normal file
|
@ -0,0 +1,523 @@
|
|||
<template>
|
||||
<div>
|
||||
<input type="file" name="media" class="d-none file-input" multiple="" v-bind:accept="config.uploader.media_types">
|
||||
<div class="timeline">
|
||||
<div class="card status-card card-md-rounded-0">
|
||||
<div class="card-header d-inline-flex align-items-center bg-white">
|
||||
<img v-bind:src="profile.avatar" width="32px" height="32px" style="border-radius: 32px;" class="box-shadow">
|
||||
<a class="username font-weight-bold pl-2 text-dark" v-bind:href="profile.url">
|
||||
{{profile.username}}
|
||||
</a>
|
||||
<div class="text-right" style="flex-grow:1;">
|
||||
<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 fa-lg text-muted"></span>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
|
||||
<div class="dropdown-item small font-weight-bold" v-on:click="createCollection">Create Collection</div>
|
||||
<div class="dropdown-divider"></div>
|
||||
<div class="dropdown-item small font-weight-bold" v-on:click="about">About</div>
|
||||
<div class="dropdown-item small font-weight-bold" v-on:click="closeModal">Close</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="postPresenterContainer">
|
||||
<div v-if="uploading">
|
||||
<div class="w-100 h-100 bg-light py-5" style="border-bottom: 1px solid #f1f1f1">
|
||||
<div class="p-5">
|
||||
<b-progress :value="uploadProgress" :max="100" striped :animated="true"></b-progress>
|
||||
<p class="text-center mb-0 font-weight-bold">Uploading ... ({{uploadProgress}}%)</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div v-if="ids.length > 0 && ids.length != config.uploader.album_limit" class="card-header py-2 bg-primary m-2 rounded cursor-pointer" v-on:click="addMedia($event)">
|
||||
<p class="text-center mb-0 font-weight-bold text-white"><i class="fas fa-plus mr-1"></i> Add Photo</p>
|
||||
</div>
|
||||
<div v-if="ids.length == 0" class="w-100 h-100 bg-light py-5 cursor-pointer" style="border-bottom: 1px solid #f1f1f1" v-on:click="addMedia($event)">
|
||||
<div class="p-5">
|
||||
<p class="text-center font-weight-bold">{{composeMessage()}}</p>
|
||||
<p class="text-muted mb-0 small text-center">Accepted Formats: <b>{{acceptedFormats()}}</b></p>
|
||||
<p class="text-muted mb-0 small text-center">Max File Size: <b>{{maxSize()}}</b></p>
|
||||
<p class="text-muted mb-0 small text-center">Albums can contain up to <b>{{config.uploader.album_limit}}</b> photos or videos</p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="ids.length > 0">
|
||||
|
||||
<b-carousel id="p-carousel"
|
||||
style="text-shadow: 1px 1px 2px #333;"
|
||||
controls
|
||||
indicators
|
||||
background="#ffffff"
|
||||
:interval="0"
|
||||
v-model="carouselCursor"
|
||||
>
|
||||
<b-carousel-slide v-if="ids.length > 0" v-for="(preview, index) in media" :key="'preview_media_'+index">
|
||||
<div slot="img" :class="[media[index].filter_class?media[index].filter_class:'']" style="display:flex;min-height: 320px;align-items: center;">
|
||||
<img class="d-block img-fluid w-100" :src="preview.url" :alt="preview.description" :title="preview.description">
|
||||
</div>
|
||||
</b-carousel-slide>
|
||||
</b-carousel>
|
||||
</div>
|
||||
<div v-if="ids.length > 0 && media[carouselCursor].type == 'Image'" class="bg-dark align-items-center">
|
||||
<ul class="nav media-drawer-filters text-center">
|
||||
<li class="nav-item">
|
||||
<div class="p-1 pt-3">
|
||||
<img :src="media[carouselCursor].url" width="100px" height="60px" v-on:click.prevent="toggleFilter($event, null)" class="cursor-pointer">
|
||||
</div>
|
||||
<a :class="[media[carouselCursor].filter_class == null ? 'nav-link text-white active' : 'nav-link text-muted']" href="#" v-on:click.prevent="toggleFilter($event, null)">No Filter</a>
|
||||
</li>
|
||||
<li class="nav-item" v-for="(filter, index) in filters">
|
||||
<div class="p-1 pt-3">
|
||||
<img :src="media[carouselCursor].url" width="100px" height="60px" :class="filter[1]" v-on:click.prevent="toggleFilter($event, filter[1])">
|
||||
</div>
|
||||
<a :class="[media[carouselCursor].filter_class == filter[1] ? 'nav-link text-white active' : 'nav-link text-muted']" href="#" v-on:click.prevent="toggleFilter($event, filter[1])">{{filter[0]}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="ids.length > 0 && ['Image', 'Video'].indexOf(media[carouselCursor].type) != -1" class="bg-lighter p-2 row">
|
||||
<div v-if="media[carouselCursor].type == 'Image'" class="col-12">
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" v-model="media[carouselCursor].alt" placeholder="Optional image description">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" v-model="media[carouselCursor].license" placeholder="Optional media license">
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="col-6 pt-2">
|
||||
<button class="btn btn-outline-secondary btn-sm mr-1"><i class="fas fa-map-marker-alt"></i></button>
|
||||
<button class="btn btn-outline-secondary btn-sm"><i class="fas fa-tools"></i></button>
|
||||
</div> -->
|
||||
<div class="col-12 text-right pt-2">
|
||||
<button class="btn btn-outline-danger btn-sm font-weight-bold mr-1" v-on:click="deleteMedia()">Delete Media</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body p-0 border-top">
|
||||
<div class="caption">
|
||||
<textarea class="form-control mb-0 border-0 rounded-0" rows="3" placeholder="Add an optional caption" v-model="composeText"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-footer">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<div class="custom-control custom-switch d-inline mr-3">
|
||||
<input type="checkbox" class="custom-control-input" id="nsfwToggle" v-model="nsfw">
|
||||
<label class="custom-control-label small font-weight-bold text-muted pt-1" for="nsfwToggle">NSFW</label>
|
||||
</div>
|
||||
<div class="dropdown d-inline">
|
||||
<button class="btn btn-outline-secondary btn-sm py-0 dropdown-toggle" type="button" id="visibility" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
{{visibility[0].toUpperCase() + visibility.slice(1)}}
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="visibility" style="width: 200px;">
|
||||
<a :class="[visibility=='public'?'dropdown-item active':'dropdown-item']" href="#" data-id="public" data-title="Public" v-on:click.prevent="visibility = 'public'">
|
||||
<div class="row">
|
||||
<div class="d-none d-block-sm col-sm-2 px-0 text-center">
|
||||
<i class="fas fa-globe"></i>
|
||||
</div>
|
||||
<div class="col-12 col-sm-10 pl-2">
|
||||
<p class="font-weight-bold mb-0">Public</p>
|
||||
<p class="small mb-0">Anyone can see</p>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<a :class="[visibility=='private'?'dropdown-item active':'dropdown-item']" href="#" data-id="private" data-title="Followers Only" v-on:click.prevent="visibility = 'private'">
|
||||
<div class="row">
|
||||
<div class="d-none d-block-sm col-sm-2 px-0 text-center">
|
||||
<i class="fas fa-lock"></i>
|
||||
</div>
|
||||
<div class="col-12 col-sm-10 pl-2">
|
||||
<p class="font-weight-bold mb-0">Followers Only</p>
|
||||
<p class="small mb-0">Only followers can see</p>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<a :class="[visibility=='unlisted'?'dropdown-item active':'dropdown-item']" href="#" data-id="private" data-title="Unlisted" v-on:click.prevent="visibility = 'unlisted'">
|
||||
<div class="row">
|
||||
<div class="d-none d-block-sm col-sm-2 px-0 text-center">
|
||||
<i class="fas fa-lock"></i>
|
||||
</div>
|
||||
<div class="col-12 col-sm-10 pl-2">
|
||||
<p class="font-weight-bold mb-0">Unlisted</p>
|
||||
<p class="small mb-0">Not listed on public timelines</p>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<!-- <a class="dropdown-item" href="#" data-id="circle" data-title="Circle">
|
||||
<div class="row">
|
||||
<div class="col-12 col-sm-2 px-0 text-center">
|
||||
<i class="far fa-circle"></i>
|
||||
</div>
|
||||
<div class="col-12 col-sm-10 pl-2">
|
||||
<p class="font-weight-bold mb-0">Circle</p>
|
||||
<p class="small mb-0">Select a circle</p>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" data-id="direct" data-title="Direct Message">
|
||||
<div class="row">
|
||||
<div class="col-12 col-sm-2 px-0 text-center">
|
||||
<i class="fas fa-envelope"></i>
|
||||
</div>
|
||||
<div class="col-12 col-sm-10 pl-2">
|
||||
<p class="font-weight-bold mb-0">Direct Message</p>
|
||||
<p class="small mb-0">Recipients only</p>
|
||||
</div>
|
||||
</div>
|
||||
</a> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="small text-muted font-weight-bold">
|
||||
{{composeText.length}} / {{config.uploader.max_caption_length}}
|
||||
</div>
|
||||
<div class="pl-md-5">
|
||||
<!-- <div class="btn-group">
|
||||
<button type="button" class="btn btn-primary btn-sm font-weight-bold" v-on:click="compose()">{{composeState[0].toUpperCase() + composeState.slice(1)}}</button>
|
||||
<button type="button" class="btn btn-primary btn-sm dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="sr-only">Toggle Dropdown</span>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a :class="[composeState == 'publish' ?'dropdown-item font-weight-bold active':'dropdown-item font-weight-bold ']" href="#" v-on:click.prevent="composeState = 'publish'">Publish now</a>
|
||||
<!- - <a :class="[composeState == 'draft' ?'dropdown-item font-weight-bold active':'dropdown-item font-weight-bold ']" href="#" v-on:click.prevent="composeState = 'draft'">Save as draft</a>
|
||||
<a :class="[composeState == 'schedule' ?'dropdown-item font-weight-bold active':'dropdown-item font-weight-bold ']" href="#" v-on:click.prevent="composeState = 'schedule'">Schedule for later</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a :class="[composeState == 'delete' ?'dropdown-item font-weight-bold active':'dropdown-item font-weight-bold ']" href="#" v-on:click.prevent="composeState = 'delete'">Delete</a> - ->
|
||||
</div>
|
||||
</div> -->
|
||||
<button class="btn btn-primary btn-sm font-weight-bold px-3" v-on:click="compose()">Publish</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style type="text/css" scoped>
|
||||
.media-drawer-filters {
|
||||
overflow-x: scroll;
|
||||
flex-wrap:unset;
|
||||
}
|
||||
.media-drawer-filters .nav-link {
|
||||
min-width:100px;
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
.media-drawer-filters .active {
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
@media (hover: none) and (pointer: coarse) {
|
||||
.media-drawer-filters::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
config: window.App.config,
|
||||
profile: {},
|
||||
composeText: '',
|
||||
composeTextLength: 0,
|
||||
nsfw: false,
|
||||
filters: [],
|
||||
ids: [],
|
||||
media: [],
|
||||
carouselCursor: 0,
|
||||
visibility: 'public',
|
||||
mediaDrawer: false,
|
||||
composeState: 'publish',
|
||||
uploading: false,
|
||||
uploadProgress: 0,
|
||||
composeType: false
|
||||
}
|
||||
},
|
||||
|
||||
beforeMount() {
|
||||
this.fetchProfile();
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.mediaWatcher();
|
||||
this.filters = [
|
||||
['1977','filter-1977'],
|
||||
['Aden','filter-aden'],
|
||||
['Amaro','filter-amaro'],
|
||||
['Ashby','filter-ashby'],
|
||||
['Brannan','filter-brannan'],
|
||||
['Brooklyn','filter-brooklyn'],
|
||||
['Charmes','filter-charmes'],
|
||||
['Clarendon','filter-clarendon'],
|
||||
['Crema','filter-crema'],
|
||||
['Dogpatch','filter-dogpatch'],
|
||||
['Earlybird','filter-earlybird'],
|
||||
['Gingham','filter-gingham'],
|
||||
['Ginza','filter-ginza'],
|
||||
['Hefe','filter-hefe'],
|
||||
['Helena','filter-helena'],
|
||||
['Hudson','filter-hudson'],
|
||||
['Inkwell','filter-inkwell'],
|
||||
['Kelvin','filter-kelvin'],
|
||||
['Kuno','filter-juno'],
|
||||
['Lark','filter-lark'],
|
||||
['Lo-Fi','filter-lofi'],
|
||||
['Ludwig','filter-ludwig'],
|
||||
['Maven','filter-maven'],
|
||||
['Mayfair','filter-mayfair'],
|
||||
['Moon','filter-moon'],
|
||||
['Nashville','filter-nashville'],
|
||||
['Perpetua','filter-perpetua'],
|
||||
['Poprocket','filter-poprocket'],
|
||||
['Reyes','filter-reyes'],
|
||||
['Rise','filter-rise'],
|
||||
['Sierra','filter-sierra'],
|
||||
['Skyline','filter-skyline'],
|
||||
['Slumber','filter-slumber'],
|
||||
['Stinson','filter-stinson'],
|
||||
['Sutro','filter-sutro'],
|
||||
['Toaster','filter-toaster'],
|
||||
['Valencia','filter-valencia'],
|
||||
['Vesper','filter-vesper'],
|
||||
['Walden','filter-walden'],
|
||||
['Willow','filter-willow'],
|
||||
['X-Pro II','filter-xpro-ii']
|
||||
];
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetchProfile() {
|
||||
axios.get('/api/v1/accounts/verify_credentials').then(res => {
|
||||
this.profile = res.data;
|
||||
if(res.data.locked == true) {
|
||||
this.visibility = 'private';
|
||||
}
|
||||
}).catch(err => {
|
||||
});
|
||||
},
|
||||
|
||||
addMedia(event) {
|
||||
let el = $(event.target);
|
||||
el.attr('disabled', '');
|
||||
let fi = $('.file-input[name="media"]');
|
||||
fi.trigger('click');
|
||||
el.blur();
|
||||
el.removeAttr('disabled');
|
||||
},
|
||||
|
||||
mediaWatcher() {
|
||||
let self = this;
|
||||
$(document).on('change', '.file-input', function(e) {
|
||||
let io = document.querySelector('.file-input');
|
||||
Array.prototype.forEach.call(io.files, function(io, i) {
|
||||
self.uploading = true;
|
||||
if(self.media && self.media.length + i >= self.config.uploader.album_limit) {
|
||||
swal('Error', 'You can only upload ' + self.config.uploader.album_limit + ' photos per album', 'error');
|
||||
return;
|
||||
}
|
||||
let type = io.type;
|
||||
let acceptedMimes = self.config.uploader.media_types.split(',');
|
||||
let validated = $.inArray(type, acceptedMimes);
|
||||
if(validated == -1) {
|
||||
swal('Invalid File Type', 'The file you are trying to add is not a valid mime type. Please upload a '+self.config.uploader.media_types+' only.', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
let form = new FormData();
|
||||
form.append('file', io);
|
||||
|
||||
let xhrConfig = {
|
||||
onUploadProgress: function(e) {
|
||||
let progress = Math.round( (e.loaded * 100) / e.total );
|
||||
self.uploadProgress = progress;
|
||||
}
|
||||
};
|
||||
|
||||
axios.post('/api/v1/media', form, xhrConfig)
|
||||
.then(function(e) {
|
||||
self.uploadProgress = 100;
|
||||
self.ids.push(e.data.id);
|
||||
self.media.push(e.data);
|
||||
setTimeout(function() {
|
||||
self.uploading = false;
|
||||
}, 1000);
|
||||
}).catch(function(e) {
|
||||
self.uploading = false;
|
||||
io.value = null;
|
||||
swal('Oops, something went wrong!', 'An unexpected error occurred.', 'error');
|
||||
});
|
||||
io.value = null;
|
||||
self.uploadProgress = 0;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
toggleFilter(e, filter) {
|
||||
this.media[this.carouselCursor].filter_class = filter;
|
||||
},
|
||||
|
||||
updateMedia() {
|
||||
this.mediaDrawer = false;
|
||||
},
|
||||
|
||||
deleteMedia() {
|
||||
if(window.confirm('Are you sure you want to delete this media?') == false) {
|
||||
return;
|
||||
}
|
||||
let id = this.media[this.carouselCursor].id;
|
||||
axios.delete('/api/v1/media', {
|
||||
params: {
|
||||
id: id
|
||||
}
|
||||
}).then(res => {
|
||||
if(this.media.length == 1) {
|
||||
this.mediaDrawer = false;
|
||||
this.ids = [];
|
||||
this.media = [];
|
||||
this.carouselCursor = 0;
|
||||
}
|
||||
this.ids.splice(this.carouselCursor, 1);
|
||||
this.media.splice(this.carouselCursor, 1);
|
||||
}).catch(err => {
|
||||
swal('Whoops!', 'An error occured when attempting to delete this, please try again', 'error');
|
||||
});
|
||||
},
|
||||
|
||||
mediaAltText() {
|
||||
return;
|
||||
// deprecate
|
||||
swal({
|
||||
text: 'Add a media description',
|
||||
content: "input"
|
||||
}).then(val => {
|
||||
let media = this.media[this.carouselCursor];
|
||||
media.alt = val;
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
mediaLicense() {
|
||||
return;
|
||||
// deprecate
|
||||
swal({
|
||||
text: 'Add a media license',
|
||||
content: "input",
|
||||
button: {
|
||||
text: "Update",
|
||||
closeModal: true,
|
||||
},
|
||||
}).then(val => {
|
||||
let media = this.media[this.carouselCursor];
|
||||
media.license = val;
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
compose() {
|
||||
let state = this.composeState;
|
||||
|
||||
if(this.uploadProgress != 100 || this.ids.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.composeText.length > this.config.uploader.max_caption_length) {
|
||||
swal('Error', 'Caption is too long', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
switch(state) {
|
||||
case 'publish' :
|
||||
if(this.media.length == 0) {
|
||||
swal('Whoops!', 'You need to add media before you can save this!', 'warning');
|
||||
return;
|
||||
}
|
||||
if(this.composeText == 'Add optional caption...') {
|
||||
this.composeText = '';
|
||||
}
|
||||
let data = {
|
||||
media: this.media,
|
||||
caption: this.composeText,
|
||||
visibility: this.visibility,
|
||||
cw: this.nsfw
|
||||
};
|
||||
axios.post('/api/local/status/compose', data)
|
||||
.then(res => {
|
||||
let data = res.data;
|
||||
window.location.href = data;
|
||||
}).catch(err => {
|
||||
let res = err.response.data;
|
||||
if(res.message == 'Too Many Attempts.') {
|
||||
swal('You\'re posting too much!', 'We only allow 50 posts per hour or 100 per day. If you\'ve reached that limit, please try again later. If you think this is an error, please contact an administrator.', 'error');
|
||||
return;
|
||||
}
|
||||
swal('Oops, something went wrong!', 'An unexpected error occurred.', 'error');
|
||||
});
|
||||
return;
|
||||
break;
|
||||
|
||||
case 'delete' :
|
||||
this.mediaDrawer = false;
|
||||
this.ids = [];
|
||||
this.media = [];
|
||||
this.carouselCursor = 0;
|
||||
this.composeText = '';
|
||||
this.composeTextLength = 0;
|
||||
$('#composeModal').modal('hide');
|
||||
return;
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
about() {
|
||||
let text = document.createElement('div');
|
||||
text.innerHTML = `
|
||||
<p class="small font-weight-bold">Please visit the <a href="/site/kb/sharing-media">Sharing Media</a> page for more info.</p>
|
||||
`;
|
||||
swal({
|
||||
title: 'Compose UI v3',
|
||||
content: text,
|
||||
icon: 'info'
|
||||
});
|
||||
},
|
||||
|
||||
closeModal() {
|
||||
this.composeType = '';
|
||||
$('#composeModal').modal('hide');
|
||||
},
|
||||
|
||||
composeMessage() {
|
||||
let config = this.config;
|
||||
let composeType = this.composeType;
|
||||
let video = config.uploader.media_types.includes('video/mp4');
|
||||
|
||||
return video ?
|
||||
'Click here to add photos or videos' :
|
||||
'Click here to add photos';
|
||||
},
|
||||
|
||||
createCollection() {
|
||||
window.location.href = '/i/collections/create';
|
||||
},
|
||||
|
||||
maxSize() {
|
||||
let limit = this.config.uploader.max_photo_size;
|
||||
return limit / 1000 + ' MB';
|
||||
},
|
||||
|
||||
acceptedFormats() {
|
||||
let formats = this.config.uploader.media_types;
|
||||
return formats.split(',').map(f => {
|
||||
return ' ' + f.split('/')[1];
|
||||
}).toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -187,11 +187,11 @@
|
|||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="mode == 'grid' != -1 && timeline.length == 0">
|
||||
<div class="py-5 text-center text-muted">
|
||||
<p><i class="fas fa-camera-retro fa-2x"></i></p>
|
||||
<p class="h2 font-weight-light pt-3">No posts yet</p>
|
||||
<div v-if="timeline.length == 0" class="col-12">
|
||||
<div class="py-5 text-center text-muted">
|
||||
<p><i class="fas fa-camera-retro fa-2x"></i></p>
|
||||
<p class="h2 font-weight-light pt-3">No posts yet</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="timeline.length && mode == 'grid'">
|
||||
|
@ -290,10 +290,10 @@
|
|||
</span>
|
||||
<span v-if="profile.id != user.id && user.hasOwnProperty('id')">
|
||||
<span class="pl-md-4 pl-sm-2" v-if="relationship.following == true">
|
||||
<button type="button" class="btn btn-outline-secondary font-weight-bold btn-sm" @click.prevent="followProfile()" data-toggle="tooltip" title="Unfollow">Unfollow</button>
|
||||
<button type="button" class="btn btn-outline-secondary font-weight-bold btn-sm" @click.prevent="followProfile()">Unfollow</button>
|
||||
</span>
|
||||
<span class="pl-md-4 pl-sm-2" v-else>
|
||||
<button type="button" class="btn btn-primary font-weight-bold btn-sm" @click.prevent="followProfile()" data-toggle="tooltip" title="Follow">Follow</button>
|
||||
<button type="button" class="btn btn-primary font-weight-bold btn-sm" @click.prevent="followProfile()">Follow</button>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
@ -960,12 +960,10 @@
|
|||
return o;
|
||||
},
|
||||
|
||||
followProfile($event) {
|
||||
followProfile() {
|
||||
if($('body').hasClass('loggedIn') == false) {
|
||||
return;
|
||||
}
|
||||
$event.target.setAttribute('disabled','');
|
||||
$event.target.blur();
|
||||
axios.post('/i/follow', {
|
||||
item: this.profileId
|
||||
}).then(res => {
|
||||
|
@ -979,7 +977,6 @@
|
|||
this.profile.followers_count++;
|
||||
}
|
||||
this.relationship.following = !this.relationship.following;
|
||||
$event.target.removeAttribute('disabled');
|
||||
}).catch(err => {
|
||||
if(err.response.data.message) {
|
||||
swal('Error', err.response.data.message, 'error');
|
||||
|
|
|
@ -57,9 +57,9 @@
|
|||
{{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>
|
||||
<button v-if="profile.entity.follow_request" type="button" class="btn btn-secondary btn-sm py-1 font-weight-bold" disabled>Follow Requested</button>
|
||||
<button v-if="!profile.entity.follow_request && profile.entity.following" type="button" class="btn btn-secondary btn-sm py-1 font-weight-bold" @click.prevent="followProfile(profile, index)">Unfollow</button>
|
||||
<button v-if="!profile.entity.follow_request && !profile.entity.following" type="button" class="btn btn-primary btn-sm py-1 font-weight-bold" @click.prevent="followProfile(profile, index)">Follow</button>
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
|
@ -140,12 +140,19 @@ export default {
|
|||
})
|
||||
},
|
||||
|
||||
followProfile(id) {
|
||||
// todo: finish AP Accept handling to enable remote follows
|
||||
followProfile(profile, index) {
|
||||
this.loading = true;
|
||||
axios.post('/i/follow', {
|
||||
item: id
|
||||
item: profile.entity.id
|
||||
}).then(res => {
|
||||
window.location.href = window.location.href;
|
||||
if(profile.entity.local == true) {
|
||||
this.fetchSearchResults();
|
||||
return;
|
||||
} else {
|
||||
this.loading = false;
|
||||
this.results.profiles[index].entity.follow_request = true;
|
||||
return;
|
||||
}
|
||||
}).catch(err => {
|
||||
if(err.response.data.message) {
|
||||
swal('Error', err.response.data.message, 'error');
|
||||
|
|
4
resources/assets/js/compose-classic.js
vendored
Normal file
4
resources/assets/js/compose-classic.js
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
Vue.component(
|
||||
'compose-classic',
|
||||
require('./components/ComposeClassic.vue').default
|
||||
);
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
@include('settings.security.device-panel')
|
||||
|
||||
@if(config('pixelfed.account_deletion') && !$user->is_admin && $user->created_at->lt(now()->subHours(12)))
|
||||
@if(config('pixelfed.account_deletion') && !$user->is_admin)
|
||||
<h4 class="font-weight-bold pt-3">Danger Zone</h4>
|
||||
<div class="mb-4 border rounded border-danger">
|
||||
<ul class="list-group mb-0 pb-0">
|
||||
|
@ -40,7 +40,6 @@
|
|||
<a class="btn btn-outline-danger font-weight-bold py-1" href="{{route('settings.remove.temporary')}}">Disable</a>
|
||||
</div>
|
||||
</li>
|
||||
@if(!$user->is_admin && $user->created_at->lt(now()->subDays(7)) )
|
||||
<li class="list-group-item border-left-0 border-right-0 py-3 d-flex justify-content-between">
|
||||
<div>
|
||||
<p class="font-weight-bold mb-1">Delete this Account</p>
|
||||
|
@ -50,7 +49,6 @@
|
|||
<a class="btn btn-outline-danger font-weight-bold py-1" href="{{route('settings.remove.permanent')}}">Delete</a>
|
||||
</div>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
|
|
@ -2,26 +2,31 @@
|
|||
|
||||
@section('content')
|
||||
|
||||
<div class="container mt-5">
|
||||
{{-- <div class="container mt-5">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6 offset-md-3">
|
||||
<p class="lead text-center font-weight-bold">The Classic Compose UI has been retired.</p>
|
||||
<p class="lead text-center font-weight-bold">Compose New Post</p>
|
||||
<p class="lead text-center">
|
||||
<a href="javascript:void(0)" class="btn btn-primary font-weight-bold" data-toggle="modal" data-target="#composeModal">New Post</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div> --}}
|
||||
|
||||
<div class="modal pr-0" tabindex="-1" role="dialog" id="composeModal">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<compose-classic></compose-classic>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script type="text/javascript" src="{{ mix('js/compose.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ mix('js/compose-classic.js') }}"></script>
|
||||
<script type="text/javascript">App.boot();</script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
new Vue({
|
||||
el: '#content'
|
||||
});
|
||||
});
|
||||
$('#composeModal').modal('show');
|
||||
</script>
|
||||
@endpush
|
|
@ -102,13 +102,13 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
|
|||
Route::get('status/{id}/replies', 'InternalApiController@statusReplies');
|
||||
Route::post('moderator/action', 'InternalApiController@modAction');
|
||||
Route::get('discover/categories', 'InternalApiController@discoverCategories');
|
||||
Route::post('status/compose', 'InternalApiController@composePost')->middleware('throttle:maxPostsPerHour,60')->middleware('throttle:maxPostsPerDay,1440');
|
||||
Route::get('loops', 'DiscoverController@loopsApi');
|
||||
Route::post('loops/watch', 'DiscoverController@loopWatch');
|
||||
Route::get('discover/tag', 'DiscoverController@getHashtags');
|
||||
Route::post('status/compose', 'InternalApiController@composePost')->middleware('throttle:maxPostsPerHour,60')->middleware('throttle:maxPostsPerDay,1440');
|
||||
});
|
||||
Route::group(['prefix' => 'local'], function () {
|
||||
Route::post('status/compose', 'InternalApiController@compose')->middleware('throttle:maxPostsPerHour,60')->middleware('throttle:maxPostsPerDay,1440');
|
||||
Route::post('status/compose', 'InternalApiController@composePost')->middleware('throttle:maxPostsPerHour,60')->middleware('throttle:maxPostsPerDay,1440');
|
||||
Route::get('exp/rec', 'ApiController@userRecommendations');
|
||||
Route::post('discover/tag/subscribe', 'HashtagFollowController@store')->middleware('throttle:maxHashtagFollowsPerHour,60')->middleware('throttle:maxHashtagFollowsPerDay,1440');;
|
||||
Route::get('discover/tag/list', 'HashtagFollowController@getTags');
|
||||
|
@ -122,6 +122,10 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
|
|||
Route::post('collection/{id}/publish', 'CollectionController@publish')->middleware('throttle:maxCollectionsPerHour,60')->middleware('throttle:maxCollectionsPerDay,1440')->middleware('throttle:maxCollectionsPerMonth,43800');
|
||||
Route::get('profile/collections/{id}', 'CollectionController@getUserCollections');
|
||||
});
|
||||
Route::group(['prefix' => 'admin'], function () {
|
||||
Route::post('moderate', 'Api\AdminApiController@moderate');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Route::get('discover/tags/{hashtag}', 'DiscoverController@showTags');
|
||||
|
|
2
webpack.mix.js
vendored
2
webpack.mix.js
vendored
|
@ -21,6 +21,7 @@ mix.js('resources/assets/js/app.js', 'public/js')
|
|||
.js('resources/assets/js/status.js', 'public/js')
|
||||
.js('resources/assets/js/timeline.js', 'public/js')
|
||||
.js('resources/assets/js/compose.js', 'public/js')
|
||||
.js('resources/assets/js/compose-classic.js', 'public/js')
|
||||
.js('resources/assets/js/search.js', 'public/js')
|
||||
.js('resources/assets/js/developers.js', 'public/js')
|
||||
.js('resources/assets/js/loops.js', 'public/js')
|
||||
|
@ -33,6 +34,7 @@ mix.js('resources/assets/js/app.js', 'public/js')
|
|||
.js('resources/assets/js/hashtag.js', 'public/js')
|
||||
.js('resources/assets/js/collectioncompose.js', 'public/js')
|
||||
.js('resources/assets/js/collections.js', 'public/js')
|
||||
//.js('resources/assets/js/admin.js', 'public/js')
|
||||
|
||||
.extract([
|
||||
'lodash',
|
||||
|
|
Loading…
Reference in a new issue