Merge pull request #1077 from pixelfed/frontend-ui-refactor

New Compose UI
pull/1105/head
daniel 8 years ago committed by GitHub
commit 59f156510d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -23,7 +23,7 @@ return [
| This value is the version of your PixelFed instance.
|
*/
'version' => '0.8.3',
'version' => '0.8.4',
/*
|--------------------------------------------------------------------------

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -3,11 +3,11 @@
"/js/app.js": "/js/app.js?id=a51da95c2b9df7cf8de2",
"/css/app.css": "/css/app.css?id=20dc108f4dbbc76c9827",
"/css/appdark.css": "/css/appdark.css?id=08d0a92912b101bb7457",
"/js/components.js": "/js/components.js?id=39d45813739697ddad5c",
"/js/components.js": "/js/components.js?id=793c50b76d7f9028f550",
"/js/compose.js": "/js/compose.js?id=902c647f5a2862676c1e",
"/js/discover.js": "/js/discover.js?id=75fb12b06ee23fa05186",
"/js/landing.js": "/js/landing.js?id=7db0170a1a3d59d798f5",
"/js/micro.js": "/js/micro.js?id=178479fb6990f8806257",
"/js/landing.js": "/js/landing.js?id=328c8bec8bde1e516430",
"/js/profile.js": "/js/profile.js?id=69fd7039c50660f4e34d",
"/js/status.js": "/js/status.js?id=2ebd1e7fd640e0f0fed3",
"/js/timeline.js": "/js/timeline.js?id=1f0cb4532c87939aada6"
"/js/status.js": "/js/status.js?id=a8289051dc914ddc013f",
"/js/timeline.js": "/js/timeline.js?id=c6a7953a0751f12efe0d"
}

@ -1,9 +1,8 @@
<template>
<div>
<input type="file" name="media" class="d-none file-input" multiple="">
<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">
@ -15,16 +14,16 @@
<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="mediaDrawer = !mediaDrawer">Show Media Toolbar</div>
<div v-show="media.length > 0" class="dropdown-item small font-weight-bold" v-on:click="mediaDrawer = !mediaDrawer">{{mediaDrawer ? 'Hide' : 'Show'}} Media Toolbar</div>
<div class="dropdown-item small font-weight-bold" v-on:click="about">About</div>
<div class="dropdown-divider"></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="ids.length == 0" class="w-100 h-100 bg-light py-5 cursor-pointer" style="border-bottom: 1px solid #f1f1f1" v-on:click="addMedia()">
<p class="text-center mb-0 font-weight-bold p-5">Click here to add photos.</p>
</div>
@ -45,21 +44,41 @@
</b-carousel-slide>
</b-carousel>
</div>
<div v-if="mediaDrawer" 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">
<div :class="filter[1]" v-on:click.prevent="toggleFilter($event, filter[1])">
<img :src="media[carouselCursor].url" width="100px" height="60px" class="">
</div>
</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 v-if="mediaDrawer" class="bg-lighter p-2 row">
<div class="col-4">
<select class="form-control form-control-sm" id="filterSelectDropdown" v-on:change="toggleFilter($event)">
<option value="none">No filter</option>
<option v-for="(filter, index) in filters" :value="filter[1]" :selected="filter[1]==media[carouselCursor].filter_class?'selected':''">{{filter[0]}}</option>
</select>
<div 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-5">
<button class="btn btn-outline-primary btn-sm mr-1" v-on:click="mediaAltText()">Alt Text</button>
<button class="btn btn-outline-primary btn-sm mr-1" v-on:click="mediaLicense()">License</button>
<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-3 text-right">
<div class="col-6 text-right pt-2">
<button class="btn btn-outline-danger btn-sm font-weight-bold mr-1" v-on:click="deleteMedia()"><i class="fas fa-trash"></i></button>
<button class="btn btn-outline-secondary btn-sm font-weight-bold" v-on:click="updateMedia()"><i class="fas fa-times"></i></button>
<button class="btn btn-outline-secondary btn-sm font-weight-bold" v-on:click="updateMedia()">Close</button>
</div>
</div>
</div>
@ -101,12 +120,12 @@
</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">
Public
{{visibility[0].toUpperCase() + visibility.slice(1)}}
</button>
<div class="dropdown-menu" aria-labelledby="visibility" style="width: 200px;">
<a class="dropdown-item active" href="#" data-id="public" data-title="Public">
<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="col-12 col-sm-2 px-0 text-center">
<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">
@ -115,9 +134,9 @@
</div>
</div>
</a>
<a class="dropdown-item" href="#" data-id="private" data-title="Followers Only">
<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="col-12 col-sm-2 px-0 text-center">
<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">
@ -126,8 +145,18 @@
</div>
</div>
</a>
<a class="dropdown-item" href="#" data-id="circle" data-title="Circle">
<a :class="[visibility=='private'?'dropdown-item active':'dropdown-item']" href="#" data-id="private" data-title="Followers Only" 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>
@ -148,7 +177,7 @@
<p class="small mb-0">Recipients only</p>
</div>
</div>
</a>
</a> -->
</div>
</div>
</div>
@ -163,10 +192,10 @@
</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 == '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>
<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>
</div>
@ -179,59 +208,57 @@
</template>
<style type="text/css" scoped>
.glass {
-webkit-filter: blur(2px);
-moz-filter: blur(2px);
-o-filter: blur(2px);
-ms-filter: blur(2px);
filter: blur(2px);
width: 100%;
height: 100%;
}
.glass {
-webkit-filter: blur(2px);
-moz-filter: blur(2px);
-o-filter: blur(2px);
-ms-filter: blur(2px);
filter: blur(2px);
width: 100%;
height: 100%;
}
.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-drawer-filters::-webkit-scrollbar {
display: none;
}
</style>
<script type="text/javascript">
export default {
data() {
return {
profile: {},
create: {
hasGeneratedSelect: false,
selectedFilter: false,
currentFilterName: false,
currentFilterClass: false
config: {
uploader: {
media_types: '',
}
},
profile: {},
composeText: '',
composeTextLength: 27,
composeTextLength: 0,
nsfw: false,
filters: [],
ids: [],
media: [],
meta: {
'id': false,
'cursor': false,
'cw': false,
'alt': null,
'filter': null,
'license': null,
'preserve_exif': false,
},
cursor: 1,
carouselCursor: 0,
visibility: 'public',
cropmode: false,
croppie: false,
limit: pixelfed.settings.maxAlbumLength,
acceptedMimes: pixelfed.settings.acceptedMimes,
mediaDrawer: false,
composeState: 'publish',
filter: {
name: null,
class: null
}
composeState: 'publish'
}
},
beforeMount() {
this.fetchConfig();
this.fetchProfile();
},
@ -294,6 +321,12 @@ export default {
methods: {
fetchConfig() {
axios.get('/api/v2/config').then(res => {
this.config = res.data;
});
},
fetchProfile() {
axios.get('/api/v1/accounts/verify_credentials').then(res => {
this.profile = res.data;
@ -321,31 +354,34 @@ export default {
$(document).on('change', '.file-input', function(e) {
let io = document.querySelector('.file-input');
Array.prototype.forEach.call(io.files, function(io, i) {
if(self.media && self.media.length + i >= self.limit) {
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 = pixelfed.settings.acceptedMimes.split(',');
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 '+pixelfed.uploader.acceptedMimes+' only.', 'error');
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 config = {
let xhrConfig = {
onUploadProgress: function(e) {
let progress = Math.round( (e.loaded * 100) / e.total );
}
};
axios.post('/api/v1/media', form, config)
axios.post('/api/v1/media', form, xhrConfig)
.then(function(e) {
self.ids.push(e.data.id);
self.media.push(e.data);
self.mediaDrawer = true;
setTimeout(function() {
self.mediaDrawer = true;
}, 1000);
}).catch(function(e) {
swal('Oops, something went wrong!', 'An unexpected error occurred.', 'error');
});
@ -354,16 +390,8 @@ export default {
});
},
toggleFilter(e) {
this.media[this.carouselCursor].filter_class = e.target.value;
// todo: deprecate
this.create.selectedFilter = true;
this.create.filterName = val;
this.create.filterClass = val;
this.create.currentFilterName = val;
this.create.currentFilterClass = val;
this.filter.class = val;
toggleFilter(e, filter) {
this.media[this.carouselCursor].filter_class = filter;
},
updateMedia() {
@ -371,14 +399,20 @@ export default {
},
deleteMedia() {
if(window.confirm('Are you sure you want to delete this photo?') == false) {
return;
}
let id = this.media[this.carouselCursor].id;
axios.delete('/api/v1/media', {
params: {
id: id
}
}).then(res => {
if(this.media.length == 0) {
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);
@ -388,6 +422,8 @@ export default {
},
mediaAltText() {
return;
// deprecate
swal({
text: 'Add a media description',
content: "input"
@ -399,6 +435,8 @@ export default {
},
mediaLicense() {
return;
// deprecate
swal({
text: 'Add a media license',
content: "input",
@ -414,23 +452,60 @@ export default {
},
compose() {
if(this.media.length == 0) {
swal('Whoops!', 'You need to add media before you can save this!', 'warning');
return;
let state = this.composeState;
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/v2/status/compose', data)
.then(res => {
let data = res.data;
window.location.href = data;
}).catch(err => {
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;
}
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 => {
swal('Oops, something went wrong!', 'An unexpected error occurred.', 'error');
},
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() {
$('#composeModal').modal('hide');
}
}
}

@ -1,56 +0,0 @@
<template>
<div></div>
</template>
<style type="text/css" scoped>
body {
background-color: #ECECEC;
}
.bg-alt {
background-color: #2C2E3B;
}
.cursor-pointer {
cursor: pointer;
}
.ql-container {
font-size: 14px;
}
</style>
<script type="text/javascript">
export default {
data() {
return {
footer: {
about: '/site/about',
help: '/site/help',
classic: '/'
},
loading: true,
profile: {},
page: 1,
activeFeed: 'home',
homeFeed: [],
notifications: {},
composeType: false,
notes: {
active: false,
status_id: null,
username: null,
like_count: 0,
share_count: 0,
replies: [],
page: 1
},
profileCard: {
account: {
avatar: null
}
},
quill: false,
searching: false
};
}
}
</script>

@ -0,0 +1,4 @@
Vue.component(
'compose-modal',
require('./components/ComposeModal.vue').default
);

@ -1,40 +0,0 @@
require('./bootstrap');
window.Vue = require('vue');
import BootstrapVue from 'bootstrap-vue'
import InfiniteLoading from 'vue-infinite-loading';
import Loading from 'vue-loading-overlay';
import VueTimeago from 'vue-timeago';
//import {Howl, Howler} from 'howler';
Vue.use(BootstrapVue);
Vue.use(InfiniteLoading);
Vue.use(Loading);
Vue.use(VueTimeago);
pixelfed.readmore = () => {
$('.read-more').each(function(k,v) {
let el = $(this);
let attr = el.attr('data-readmore');
if(typeof attr !== typeof undefined && attr !== false) {
return;
}
el.readmore({
collapsedHeight: 44,
heightMargin: 20,
moreLink: '<a href="#" class="font-weight-bold small">Read more</a>',
lessLink: '<a href="#" class="font-weight-bold small">Hide</a>',
});
});
};
try {
document.createEvent("TouchEvent");
$('body').addClass('touch');
} catch (e) {
}
Vue.component(
'micro',
require('./components/Micro.vue').default
);

@ -315,12 +315,6 @@ body, button, input, textarea {
text-align: center;
border-radius: 0 3px 3px 0;
}
.reactions button {
font-size: 1.575rem;
line-height: 1;
}
}
.touch .card {

@ -23,4 +23,16 @@
</div>
</div>
@endsection
@endsection
@push('scripts')
<script type="text/javascript">
$(document).ready(function() {
$('a[data-target="#composeModal"]').on('click', function() {
$('#composeModal').modal('hide');
swal('Not Supported', 'You cannot compose a post from the admin dashboard.', 'error');
return;
});
});
</script>
@endpush

@ -24,4 +24,14 @@
</div>
</div>
@endsection
@endsection
@push('scripts')
<script type="text/javascript">
$(document).ready(function() {
$('a#composeModal').on('click', function() {
swal('Not Supported', 'You cannot compose a post from the admin dashboard.', 'error');
});
});
</script>
@endpush

@ -10,6 +10,7 @@
@push('scripts')
<script type="text/javascript" src="{{ mix('js/discover.js') }}"></script>
<script type="text/javascript" src="{{ mix('js/compose.js') }}"></script>
<script type="text/javascript">
$(document).ready(function(){new Vue({el: '#content'});});
</script>

@ -1,7 +1,7 @@
@extends('layouts.app')
@section('content')
<div class="profile-header bg-light" style="background: #FF5F6D; /* fallback for old browsers */
<div class="profile-header" style="background: #FF5F6D; /* fallback for old browsers */
background: -webkit-linear-gradient(to right, #FFC371, #FF5F6D); /* Chrome 10-25, Safari 5.1-6 */
background: linear-gradient(to right, #FFC371, #FF5F6D); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
">
@ -54,4 +54,11 @@ background: linear-gradient(to right, #FFC371, #FF5F6D); /* W3C, IE 10+/ Edge, F
</div>
</div>
@endsection
@endsection
@push('scripts')
<script type="text/javascript" src="{{ mix('js/compose.js') }}"></script>
<script type="text/javascript">
$(document).ready(function(){new Vue({el: '#content'});});
</script>
@endpush

@ -83,3 +83,10 @@
}
</style>
@endpush
@push('scripts')
<script type="text/javascript" src="{{ mix('js/compose.js') }}"></script>
<script type="text/javascript">
$(document).ready(function(){new Vue({el: '#content'});});
</script>
@endpush

@ -45,3 +45,9 @@
@endsection
@push('scripts')
<script type="text/javascript" src="{{ mix('js/compose.js') }}"></script>
<script type="text/javascript">
$(document).ready(function(){new Vue({el: '#content'});});
</script>
@endpush

@ -32,6 +32,15 @@
@include('layouts.partial.nav')
<main id="content">
@yield('content')
@if(Auth::check())
<div class="modal pr-0" tabindex="-1" role="dialog" id="composeModal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<compose-modal></compose-modal>
</div>
</div>
</div>
@endif
</main>
@include('layouts.partial.footer')
<script type="text/javascript" src="{{ mix('js/app.js') }}"></script>
@ -60,13 +69,6 @@
</ul>
</div>
</div>
<div class="modal" tabindex="-1" role="dialog" id="composeModal">
<div class="modal-dialog" role="document">
<div class="modal-content">
@include('timeline.partial.new-form')
</div>
</div>
</div>
@endif
</body>
</html>

@ -21,6 +21,7 @@
@push('scripts')
<script type="text/javascript" src="{{ mix('js/profile.js') }}"></script>
<script type="text/javascript" src="{{ mix('js/compose.js') }}"></script>
<script type="text/javascript">
new Vue({
el: '#content'

@ -23,6 +23,7 @@
@push('scripts')
<script type="text/javascript" src="{{ mix('js/status.js') }}"></script>
<script type="text/javascript" src="{{ mix('js/compose.js') }}"></script>
<script type="text/javascript">
$(document).ready(function() {
new Vue({

@ -8,6 +8,7 @@
@push('scripts')
<script type="text/javascript" src="{{ mix('js/timeline.js') }}"></script>
<script type="text/javascript" src="{{ mix('js/compose.js') }}"></script>
<script type="text/javascript">
new Vue({
el: '#content'

@ -8,6 +8,7 @@
@push('scripts')
<script type="text/javascript" src="{{ mix('js/timeline.js') }}"></script>
<script type="text/javascript" src="{{ mix('js/compose.js') }}"></script>
<script type="text/javascript">
new Vue({
el: '#content'

@ -1,76 +1 @@
<div class="card card-md-rounded-0">
<div class="card-header bg-white font-weight-bold d-inline-flex justify-content-between">
<div>{{__('Create New Post')}}</div>
</div>
<div class="card-body" id="statusForm">
<form method="post" action="{{route('timeline.personal')}}" enctype="multipart/form-data">
@csrf
<input type="hidden" name="filter_name" value="">
<input type="hidden" name="filter_class" value="">
<div class="form-group">
<div class="custom-file">
<input type="file" class="custom-file-input" id="fileInput" name="photo[]" accept="{{config('pixelfed.media_types')}}" multiple="">
<label class="custom-file-label" for="fileInput">Upload Image(s)</label>
</div>
<small class="form-text text-muted">
Max Size: @maxFileSize(). Supported formats: jpeg, png, gif, bmp. Limited to {{config('pixelfed.max_album_length')}} photos per post.
</small>
</div>
<div class="form-group">
<textarea class="form-control" name="caption" placeholder="Add optional caption here" autocomplete="off" data-limit="{{config('pixelfed.max_caption_length')}}" rows="1"></textarea>
<p class="form-text text-muted small text-right">
<span class="caption-counter">0</span>
<span>/</span>
<span>{{config('pixelfed.max_caption_length')}}</span>
</p>
</div>
<div class="form-group">
<button class="btn btn-outline-primary btn-sm px-3 py-1 font-weight-bold" type="button" data-toggle="collapse" data-target="#collapsePreview" aria-expanded="false" aria-controls="collapsePreview">
Options &nbsp; <i class="fas fa-chevron-down"></i>
</button>
<div class="collapse" id="collapsePreview">
<div class="form-group pt-3">
<label class="font-weight-bold text-muted small">Visibility</label>
<div class="switch switch-sm">
<select class="form-control" name="visibility">
<option value="public" selected="">Public</option>
<option value="unlisted">Unlisted (hidden from public timelines)</option>
<option value="private">Followers Only</option>
</select>
</div>
<small class="form-text text-muted">
Set the visibility of this post.
</small>
</div>
<div class="form-group">
<label class="font-weight-bold text-muted small">CW/NSFW</label>
<div class="switch switch-sm">
<input type="checkbox" class="switch" id="cw-switch" name="cw">
<label for="cw-switch" class="small font-weight-bold">(Default off)</label>
</div>
<small class="form-text text-muted">
Please mark all NSFW and controversial content, as per our content policy.
</small>
</div>
<div class="form-group d-none form-preview">
<label class="font-weight-bold text-muted small">Photo Preview</label>
<figure class="filterContainer">
<img class="filterPreview img-fluid">
</figure>
<small class="form-text text-muted font-weight-bold">
No filter selected.
</small>
</div>
<div class="form-group d-none form-filters">
<label for="filterSelectDropdown" class="font-weight-bold text-muted small">Select Filter</label>
<select class="form-control" id="filterSelectDropdown">
<option value="none" selected="">No Filter</option>
</select>
</div>
</div>
</div>
<button type="submit" class="btn btn-outline-primary btn-block font-weight-bold">Create Post</button>
</form>
</div>
</div>
<compose-modal></compose-modal>

@ -95,6 +95,7 @@ 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');
});
Route::group(['prefix' => 'local'], function () {
Route::get('i/follow-suggestions', 'ApiController@followSuggestions');

6
webpack.mix.js vendored

@ -28,12 +28,12 @@ mix.js('resources/assets/js/app.js', 'public/js')
// Timeline component
.js('resources/assets/js/timeline.js', 'public/js')
// MicroUI component
.js('resources/assets/js/micro.js', 'public/js')
// LandingPage component
.js('resources/assets/js/landing.js', 'public/js')
// ComposeModal component
.js('resources/assets/js/compose.js', 'public/js')
.sass('resources/assets/sass/app.scss', 'public/css', {
implementation: require('node-sass')
})

Loading…
Cancel
Save