forked from AkkomaGang/akkoma-fe
Merge branch 'develop' into feature/hash-routed
This commit is contained in:
commit
290180a987
23 changed files with 389 additions and 202 deletions
|
@ -17,9 +17,9 @@
|
|||
"babel-plugin-lodash": "^3.2.11",
|
||||
"diff": "^3.0.1",
|
||||
"karma-mocha-reporter": "^2.2.1",
|
||||
"lz-string": "^1.4.4",
|
||||
"node-sass": "^3.10.1",
|
||||
"object-path": "^0.11.3",
|
||||
"pako": "^1.0.4",
|
||||
"sanitize-html": "^1.13.0",
|
||||
"sass-loader": "^4.0.2",
|
||||
"tributejs": "^2.1.0",
|
||||
|
@ -84,7 +84,7 @@
|
|||
"sinon-chai": "^2.8.0",
|
||||
"url-loader": "^0.5.7",
|
||||
"vue-loader": "^11.1.0",
|
||||
"vue-style-loader": "^1.0.0",
|
||||
"vue-style-loader": "^2.0.0",
|
||||
"webpack": "^1.13.2",
|
||||
"webpack-dev-middleware": "^1.8.3",
|
||||
"webpack-hot-middleware": "^2.12.2",
|
||||
|
|
49
src/App.scss
49
src/App.scss
|
@ -32,9 +32,10 @@ a {
|
|||
button{
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: white;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,11 +103,11 @@ main-router {
|
|||
flex-direction: column;
|
||||
margin: 0.5em;
|
||||
|
||||
border-radius: 0.5em;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.panel-heading {
|
||||
border-radius: 0.5em 0.5em 0 0;
|
||||
border-radius: 10px 10px 0 0;
|
||||
background-size: cover;
|
||||
padding: 0.6em 0;
|
||||
text-align: center;
|
||||
|
@ -115,7 +116,7 @@ main-router {
|
|||
}
|
||||
|
||||
.panel-footer {
|
||||
border-radius: 0 0 0.5em 0.5em;
|
||||
border-radius: 0 0 10px 10px;
|
||||
}
|
||||
|
||||
.panel-body > p {
|
||||
|
@ -128,7 +129,7 @@ main-router {
|
|||
#content {
|
||||
margin: auto;
|
||||
max-width: 980px;
|
||||
border-radius: 1em;
|
||||
border-radius: 10px;
|
||||
padding-bottom: 1em;
|
||||
background-color: rgba(0,0,0,0.1);
|
||||
}
|
||||
|
@ -142,46 +143,10 @@ main-router {
|
|||
min-width: 0px;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
color: white;
|
||||
padding: 1em;
|
||||
img {
|
||||
border: 2px solid;
|
||||
border-radius: 0.5em
|
||||
}
|
||||
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.5);
|
||||
.user-name{
|
||||
margin-top: 0.2em;
|
||||
}
|
||||
.user-screen-name {
|
||||
margin-top: 0.3em;
|
||||
font-weight: lighter;
|
||||
padding-right: 0.1em;
|
||||
}
|
||||
}
|
||||
|
||||
.user-counts {
|
||||
display: flex;
|
||||
line-height:16px;
|
||||
padding: 1em 1.5em 0em 1em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.user-count {
|
||||
flex: 1;
|
||||
|
||||
h5 {
|
||||
font-size:1em;
|
||||
font-weight: bolder;
|
||||
margin: 0 0 0.25em;
|
||||
}
|
||||
}
|
||||
|
||||
.fa {
|
||||
color: grey;
|
||||
}
|
||||
|
||||
|
||||
.status-actions {
|
||||
width: 50%;
|
||||
display: flex;
|
||||
|
@ -210,7 +175,7 @@ status-text-container {
|
|||
margin-top: 0.2em;
|
||||
float: right;
|
||||
margin-right: 0.3em;
|
||||
border-radius: 20%;
|
||||
border-radius: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,16 +7,19 @@ const Attachment = {
|
|||
'nsfw',
|
||||
'statusId'
|
||||
],
|
||||
data: () => ({
|
||||
nsfwImage,
|
||||
showHidden: false
|
||||
}),
|
||||
data () {
|
||||
return {
|
||||
nsfwImage,
|
||||
hideNsfwLocal: this.$store.state.config.hideNsfw,
|
||||
showHidden: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
type () {
|
||||
return fileTypeService.fileType(this.attachment.mimetype)
|
||||
},
|
||||
hidden () {
|
||||
return this.nsfw && !this.showHidden
|
||||
return this.nsfw && this.hideNsfwLocal && !this.showHidden
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<a class="image-attachment" v-if="hidden" v-on:click.prevent="toggleHidden()">
|
||||
<img :key="nsfwImage" :src="nsfwImage"></img>
|
||||
</a>
|
||||
<div class="hider" v-if="nsfw && !hidden">
|
||||
<div class="hider" v-if="nsfw && hideNsfwLocal && !hidden">
|
||||
<a href="#" @click.prevent="toggleHidden()">Hide</a>
|
||||
</div>
|
||||
|
||||
|
@ -16,8 +16,6 @@
|
|||
|
||||
<audio v-if="type === 'audio'" :src="attachment.url" controls></audio>
|
||||
|
||||
<span v-if="type === 'unknown'">Don't know how to display this...</span>
|
||||
|
||||
<div @click.prevent="linkClicked" v-if="type === 'html' && attachment.oembed" class="oembed">
|
||||
<div v-if="attachment.thumb_url" class="image">
|
||||
<img :src="attachment.thumb_url"></img>
|
||||
|
@ -36,14 +34,15 @@
|
|||
.attachments {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-right: -0.8em;
|
||||
.attachment {
|
||||
flex: 1 0 30%;
|
||||
display: flex;
|
||||
margin: 0.5em 0.8em 0.6em 0.1em;
|
||||
margin: 0.5em 0.8em 0.6em 0.0em;
|
||||
align-self: flex-start;
|
||||
|
||||
&.html {
|
||||
flex-basis: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.hider {
|
||||
|
@ -51,14 +50,14 @@
|
|||
margin: 10px;
|
||||
padding: 5px;
|
||||
background: rgba(230,230,230,0.6);
|
||||
border-radius: 0.5em;
|
||||
border-radius: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
video {
|
||||
height: 100%;
|
||||
border: 1px solid;
|
||||
border-radius: 0.5em;
|
||||
border-radius: 5px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
@ -71,7 +70,7 @@
|
|||
height: 100%;
|
||||
flex: 1;
|
||||
border: 1px solid;
|
||||
border-radius: 0.5em;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
|
||||
|
@ -91,7 +90,7 @@
|
|||
flex: 1;
|
||||
img {
|
||||
border: 0px;
|
||||
border-radius: 0;
|
||||
border-radius: 5px;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
@ -115,7 +114,7 @@
|
|||
width: 100%;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-radius: 0.5em;
|
||||
border-radius: 5px;
|
||||
width: 100%;
|
||||
height: 100%; /* If this isn't here, chrome will stretch the images */
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ const DeleteButton = {
|
|||
props: [ 'status' ],
|
||||
methods: {
|
||||
deleteStatus () {
|
||||
const confirmed = confirm('Do you really want to delete this status?')
|
||||
const confirmed = window.confirm('Do you really want to delete this status?')
|
||||
if (confirmed) {
|
||||
this.$store.dispatch('deleteStatus', { id: this.status.id })
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ const DeleteButton = {
|
|||
},
|
||||
computed: {
|
||||
currentUser () { return this.$store.state.users.currentUser },
|
||||
canDelete () { return this.currentUser.rights.delete_others_notice || this.status.user.id == this.currentUser.id }
|
||||
canDelete () { return this.currentUser.rights.delete_others_notice || this.status.user.id === this.currentUser.id }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<template>
|
||||
<div class="login panel panel-default base00-background">
|
||||
<!-- Default panel contents -->
|
||||
<div class="panel-heading base01-background base04">
|
||||
<div class="panel-heading base01-background">
|
||||
Log in
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form v-on:submit.prevent='submit(user)'>
|
||||
<form v-on:submit.prevent='submit(user)' class='login-form'>
|
||||
<div class='form-group'>
|
||||
<label for='username'>Username</label>
|
||||
<input :disabled="loggingIn" v-model='user.username' class='form-control' id='username' placeholder='e.g. lain'>
|
||||
|
@ -15,7 +15,7 @@
|
|||
<input :disabled="loggingIn" v-model='user.password' class='form-control' id='password' type='password'>
|
||||
</div>
|
||||
<div class='form-group'>
|
||||
<button :disabled="loggingIn" type='submit' class='btn btn-default'>Submit</button>
|
||||
<button :disabled="loggingIn" type='submit' class='btn btn-default base05 base01-background'>Submit</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -23,3 +23,22 @@
|
|||
</template>
|
||||
|
||||
<script src="./login_form.js" ></script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
.login-form {
|
||||
input {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: silver;
|
||||
border-radius: 5px;
|
||||
padding: 0.1em 0.2em 0.2em 0.2em;
|
||||
}
|
||||
|
||||
.btn {
|
||||
margin-top: 1.0em;
|
||||
min-height: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -3,12 +3,22 @@ import statusPosterService from '../../services/status_poster/status_poster.serv
|
|||
|
||||
const mediaUpload = {
|
||||
mounted () {
|
||||
const store = this.$store
|
||||
const input = this.$el.querySelector('input')
|
||||
const self = this
|
||||
|
||||
input.addEventListener('change', ({target}) => {
|
||||
const file = target.files[0]
|
||||
this.uploadFile(file)
|
||||
})
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
uploading: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
uploadFile (file) {
|
||||
const self = this
|
||||
const store = this.$store
|
||||
const formData = new FormData()
|
||||
formData.append('media', file)
|
||||
|
||||
|
@ -19,15 +29,34 @@ const mediaUpload = {
|
|||
.then((fileData) => {
|
||||
self.$emit('uploaded', fileData)
|
||||
self.uploading = false
|
||||
}, (error) => {
|
||||
}, (error) => { // eslint-disable-line handle-callback-err
|
||||
self.$emit('upload-failed')
|
||||
self.uploading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
fileDrop (e) {
|
||||
if (e.dataTransfer.files.length > 0) {
|
||||
e.preventDefault() // allow dropping text like before
|
||||
this.uploadFile(e.dataTransfer.files[0])
|
||||
}
|
||||
},
|
||||
fileDrag (e) {
|
||||
let types = e.dataTransfer.types
|
||||
if (types.contains('Files')) {
|
||||
e.dataTransfer.dropEffect = 'copy'
|
||||
} else {
|
||||
e.dataTransfer.dropEffect = 'none'
|
||||
}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
uploading: false
|
||||
props: [
|
||||
'dropFiles'
|
||||
],
|
||||
watch: {
|
||||
'dropFiles': function (fileInfos) {
|
||||
if (!this.uploading) {
|
||||
this.uploadFile(fileInfos[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="media-upload">
|
||||
<div class="media-upload" @drop.prevent @dragover.prevent="fileDrag" @drop="fileDrop">
|
||||
<label class="btn btn-default">
|
||||
<i class="fa icon-spin4 animate-spin" v-if="uploading"></i>
|
||||
<i class="fa icon-upload" v-if="!uploading"></i>
|
||||
|
|
|
@ -1,45 +1,65 @@
|
|||
@import '../../_variables.scss';
|
||||
.notification {
|
||||
padding: 0.4em 0 0 0.7em;
|
||||
display: flex;
|
||||
border-bottom: 1px solid silver;
|
||||
|
||||
.text {
|
||||
min-width: 0px;
|
||||
word-wrap: break-word;
|
||||
line-height:18px;
|
||||
.notifications {
|
||||
|
||||
.icon-retweet {
|
||||
color: $green;
|
||||
}
|
||||
|
||||
.icon-reply {
|
||||
color: $blue;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0 0 0.3em;
|
||||
padding: 0;
|
||||
font-size: 1em;
|
||||
line-height:20px;
|
||||
}
|
||||
|
||||
padding: 0.3em 0.8em 0.5em;
|
||||
p {
|
||||
margin: 0;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.3em;
|
||||
.panel-heading {
|
||||
// force the text to stay centered, while keeping
|
||||
// the button in the right side of the panel heading
|
||||
position: relative;
|
||||
button {
|
||||
position: absolute;
|
||||
padding: 0.1em 0.3em 0.25em 0.3em;
|
||||
right: 0.6em;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar {
|
||||
padding-top: 0.3em;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
.unseen {
|
||||
border-left: 4px solid rgba(255, 48, 16, 0.65);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border: none
|
||||
.notification {
|
||||
padding: 0.4em 0 0 0.7em;
|
||||
display: flex;
|
||||
border-bottom: 1px solid silver;
|
||||
|
||||
|
||||
.text {
|
||||
min-width: 0px;
|
||||
word-wrap: break-word;
|
||||
line-height:18px;
|
||||
|
||||
.icon-retweet {
|
||||
color: $green;
|
||||
}
|
||||
|
||||
.icon-reply {
|
||||
color: $blue;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0 0 0.3em;
|
||||
padding: 0;
|
||||
font-size: 1em;
|
||||
line-height:20px;
|
||||
}
|
||||
|
||||
padding: 0.3em 0.8em 0.5em;
|
||||
p {
|
||||
margin: 0;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.3em;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar {
|
||||
padding-top: 0.3em;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border: none
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
<template>
|
||||
<div class="notifications">
|
||||
<div class="panel panel-default base00-background">
|
||||
<div class="panel-heading base01-background base04">Notifications ({{unseenCount}}) <button @click.prevent="markAsSeen">Read!</button></div>
|
||||
<div class="panel-heading base01-background base04">
|
||||
Notifications ({{unseenCount}})
|
||||
<button @click.prevent="markAsSeen" class="base05 base02-background">Read!</button>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div v-for="notification in visibleNotifications" class="notification" :class='{"base01-background": notification.seen}'>
|
||||
<div v-for="notification in visibleNotifications" class="notification" :class='{"unseen": !notification.seen}'>
|
||||
<a :href="notification.action.user.statusnet_profile_url">
|
||||
<img class='avatar' :src="notification.action.user.profile_image_url_original">
|
||||
</a>
|
||||
|
|
|
@ -49,7 +49,7 @@ const defaultCollection = {
|
|||
menuContainer: document.body,
|
||||
|
||||
// column to search against in the object (accepts function or string)
|
||||
lookup: ({name, screen_name}) => `${name} (@${screen_name})`,
|
||||
lookup: ({name, screen_name}) => `${name} (@${screen_name})`, // eslint-disable-line camelcase
|
||||
|
||||
// column that contains the content to insert by default
|
||||
fillAttr: 'screen_name',
|
||||
|
@ -84,6 +84,7 @@ const PostStatusForm = {
|
|||
}
|
||||
|
||||
return {
|
||||
dropFiles: [],
|
||||
submitDisabled: false,
|
||||
newStatus: {
|
||||
status: statusText,
|
||||
|
@ -141,6 +142,15 @@ const PostStatusForm = {
|
|||
},
|
||||
type (fileInfo) {
|
||||
return fileTypeService.fileType(fileInfo.mimetype)
|
||||
},
|
||||
fileDrop (e) {
|
||||
if (e.dataTransfer.files.length > 0) {
|
||||
e.preventDefault() // allow dropping text like before
|
||||
this.dropFiles = e.dataTransfer.files
|
||||
}
|
||||
},
|
||||
fileDrag (e) {
|
||||
e.dataTransfer.dropEffect = 'copy'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,20 +2,20 @@
|
|||
<div class="post-status-form">
|
||||
<form @submit.prevent="postStatus(newStatus)">
|
||||
<div class="form-group" >
|
||||
<textarea v-model="newStatus.status" placeholder="Just landed in L.A." rows="3" class="form-control" @keyup.ctrl.enter="postStatus(newStatus)"></textarea>
|
||||
<textarea v-model="newStatus.status" placeholder="Just landed in L.A." rows="3" class="form-control" @keyup.meta.enter="postStatus(newStatus)" @keyup.ctrl.enter="postStatus(newStatus)" @drop="fileDrop" @dragover.prevent="fileDrag"></textarea>
|
||||
</div>
|
||||
<div class="attachments">
|
||||
<div class="attachment" v-for="file in newStatus.files">
|
||||
<i class="fa icon-cancel" @click="removeMediaFile(file)"></i>
|
||||
<img class="thumbnail media-upload" :src="file.image" v-if="type(file) === 'image'"></img>
|
||||
<video v-if="type(file) === 'video'" :src="file.image" controls></video>
|
||||
<audio v-if="type(file) === 'audio'" :src="file.image" controls></audio>
|
||||
<a v-if="type(file) === 'unknown'" :href="file.image">{{file.url}}</a>
|
||||
<i class="fa icon-cancel" @click="removeMediaFile(file)"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class='form-bottom'>
|
||||
<media-upload @uploading="disableSubmit" @uploaded="addMediaFile" @upload-failed="enableSubmit"></media-upload>
|
||||
<button :disabled="submitDisabled" type="submit" class="btn btn-default">Submit</button>
|
||||
<media-upload @uploading="disableSubmit" @uploaded="addMediaFile" @upload-failed="enableSubmit" :drop-files="dropFiles"></media-upload>
|
||||
<button :disabled="submitDisabled" type="submit" class="btn btn-default base05 base01-background">Submit</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -52,6 +52,15 @@
|
|||
|
||||
.attachments {
|
||||
padding: 0.5em;
|
||||
|
||||
i {
|
||||
position: absolute;
|
||||
margin: 10px;
|
||||
padding: 5px;
|
||||
background: rgba(230,230,230,0.6);
|
||||
border-radius: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
form {
|
||||
|
@ -68,10 +77,12 @@
|
|||
}
|
||||
|
||||
form textarea {
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
border: solid;
|
||||
border-width: 1px;
|
||||
border-color: silver;
|
||||
border-radius: 5px;
|
||||
line-height:16px;
|
||||
padding: 0.5em;
|
||||
padding: 5px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,22 @@
|
|||
import StyleSwitcher from '../style_switcher/style_switcher.vue'
|
||||
|
||||
const settings = {
|
||||
data () {
|
||||
return {
|
||||
hideAttachmentsLocal: this.$store.state.config.hideAttachments,
|
||||
hideNsfwLocal: this.$store.state.config.hideNsfw
|
||||
}
|
||||
},
|
||||
components: {
|
||||
StyleSwitcher
|
||||
},
|
||||
watch: {
|
||||
hideAttachmentsLocal (value) {
|
||||
this.$store.dispatch('setOption', { name: 'hideAttachments', value })
|
||||
},
|
||||
hideNsfwLocal (value) {
|
||||
this.$store.dispatch('setOption', { name: 'hideNsfw', value })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,13 @@
|
|||
<h2>Theme</h2>
|
||||
<style-switcher></style-switcher>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<h2>Attachments</h2>
|
||||
<input type="checkbox" id="hideAttachments" v-model="hideAttachmentsLocal">
|
||||
<label for="hideAttachments">Hide Attachments</label>
|
||||
<input type="checkbox" id="hideNsfw" v-model="hideNsfwLocal">
|
||||
<label for="hideNsfw">Enable clickthrough NSFW attachment hiding</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -17,6 +17,7 @@ const Status = {
|
|||
userExpanded: false
|
||||
}),
|
||||
computed: {
|
||||
hideAttachments () { return this.$store.state.config.hideAttachments },
|
||||
retweet () { return !!this.statusoid.retweeted_status },
|
||||
retweeter () { return this.statusoid.user.name },
|
||||
status () {
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
<div @click.prevent="linkClicked" class="status-content" v-html="status.statusnet_html"></div>
|
||||
|
||||
<div v-if='status.attachments' class='attachments'>
|
||||
<attachment :status-id="status.id" :nsfw="status.nsfw" :attachment="attachment" v-for="attachment in status.attachments">
|
||||
<attachment v-if="!hideAttachments" :status-id="status.id" :nsfw="status.nsfw" :attachment="attachment" v-for="attachment in status.attachments">
|
||||
</attachment>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -157,7 +157,8 @@
|
|||
.usercard {
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-radius: 1em;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 1em;
|
||||
margin-top: 0.2em;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,40 +1,42 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="base00-background panel-heading text-center" v-bind:style="style">
|
||||
<div id="heading" class="profile-panel-background" :style="headingStyle">
|
||||
<div class="panel-heading text-center">
|
||||
<div class='user-info'>
|
||||
<img :src="user.profile_image_url">
|
||||
<span class="glyphicon glyphicon-user"></span>
|
||||
<div class='user-name'>{{user.name}}</div>
|
||||
<div class='user-screen-name'>@{{user.screen_name}}</div>
|
||||
<div class='container'>
|
||||
<img :src="user.profile_image_url">
|
||||
<span class="glyphicon glyphicon-user"></span>
|
||||
<div class='user-name'>{{user.name}}</div>
|
||||
<div class='user-screen-name'>@{{user.screen_name}}</div>
|
||||
</div>
|
||||
<div v-if="isOtherUser" class="user-interactions">
|
||||
<div v-if="user.follows_you" class="following base06">
|
||||
<div v-if="user.follows_you && loggedIn" class="following base06">
|
||||
Follows you!
|
||||
</div>
|
||||
<div class="follow">
|
||||
<div class="follow" v-if="loggedIn">
|
||||
<span v-if="user.following">
|
||||
<!--Following them!-->
|
||||
<button @click="unfollowUser" class="base06 base01-background base06-border">
|
||||
Unfollow
|
||||
<button @click="unfollowUser" class="base04 base00-background pressed">
|
||||
Following!
|
||||
</button>
|
||||
</span>
|
||||
<span v-if="!user.following">
|
||||
<button @click="followUser" class="base01 base04-background base01-border">
|
||||
<button @click="followUser" class="base05 base02-background">
|
||||
Follow
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<div class='mute' v-if='isOtherUser'>
|
||||
<span v-if='user.muted'>
|
||||
<button @click="toggleMute" class="base04 base01-background base06-border">Unmute</button>
|
||||
<button @click="toggleMute" class="base04 base00-background pressed">Muted</button>
|
||||
</span>
|
||||
<span v-if='!user.muted'>
|
||||
<button @click="toggleMute" class="base01 base04-background base01-border">Mute</button>
|
||||
<button @click="toggleMute" class="base05 base02-background">Mute</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body base00-background">
|
||||
<div class="panel-body profile-panel-body" :style="bodyStyle">
|
||||
<div class="user-counts">
|
||||
<div class="user-count">
|
||||
<h5>Statuses</h5>
|
||||
|
@ -58,14 +60,25 @@
|
|||
export default {
|
||||
props: [ 'user' ],
|
||||
computed: {
|
||||
style () {
|
||||
headingStyle () {
|
||||
let rgb = this.$store.state.config.colors['base00'].match(/\d+/g)
|
||||
return {
|
||||
color: `#${this.user.profile_link_color}`,
|
||||
'background-image': `url(${this.user.cover_photo})`
|
||||
backgroundColor: 'rgb(' + Math.floor(rgb[0] * 0.53) + ', ' +
|
||||
Math.floor(rgb[1] * 0.56) + ', ' +
|
||||
Math.floor(rgb[2] * 0.59) + ')',
|
||||
backgroundImage: `url(${this.user.cover_photo})`
|
||||
}
|
||||
},
|
||||
bodyStyle () {
|
||||
return {
|
||||
background: 'linear-gradient(to bottom, rgba(0, 0, 0, 0), ' + this.$store.state.config.colors['base00'] + ' 80%)'
|
||||
}
|
||||
},
|
||||
isOtherUser () {
|
||||
return this.user !== this.$store.state.users.currentUser
|
||||
},
|
||||
loggedIn () {
|
||||
return this.$store.state.users.currentUser
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -87,3 +100,118 @@
|
|||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
.profile-panel-background {
|
||||
background-size: cover;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.profile-panel-body {
|
||||
padding-top: 0em;
|
||||
top: -0em;
|
||||
padding-top: 4em;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
color: white;
|
||||
padding: 16px 16px 16px 16px;
|
||||
margin-bottom: -4em;
|
||||
|
||||
.container{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: column;
|
||||
align-content: flex-start;
|
||||
justify-content: center;
|
||||
max-height: 60px;
|
||||
}
|
||||
|
||||
img {
|
||||
border: 2px solid;
|
||||
border-radius: 5px;
|
||||
flex: 1 0 100%;
|
||||
max-width: 48px;
|
||||
max-height: 48px;
|
||||
}
|
||||
|
||||
text-shadow: 0px 1px 1.5px rgba(0, 0, 0, 1.0);
|
||||
|
||||
.user-name{
|
||||
margin-top: 0.0em;
|
||||
margin-left: 0.6em;
|
||||
flex: 0 0 auto;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.user-screen-name {
|
||||
margin-top: 0.0em;
|
||||
margin-left: 0.6em;
|
||||
font-weight: lighter;
|
||||
font-size: 15px;
|
||||
padding-right: 0.1em;
|
||||
flex: 0 0 auto;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.user-interactions {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: space-between;
|
||||
|
||||
div {
|
||||
flex: 1;
|
||||
}
|
||||
margin-top: 0.7em;
|
||||
margin-bottom: -1.0em;
|
||||
|
||||
.following {
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
flex: 0 0 100%;
|
||||
margin: -0.7em 0.0em 0.3em 0.0em;
|
||||
padding-left: 16px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.mute {
|
||||
max-width: 220px;
|
||||
min-height: 28px;
|
||||
}
|
||||
|
||||
.follow {
|
||||
max-width: 220px;
|
||||
min-height: 28px;
|
||||
}
|
||||
|
||||
button {
|
||||
border: solid;
|
||||
border-width: 1px;
|
||||
width: 92%;
|
||||
height: 100%;
|
||||
}
|
||||
.pressed {
|
||||
border: solid;
|
||||
border-width: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-counts {
|
||||
display: flex;
|
||||
line-height:16px;
|
||||
padding: 1em 1.5em 0em 1em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.user-count {
|
||||
flex: 1;
|
||||
|
||||
h5 {
|
||||
font-size:1em;
|
||||
font-weight: bolder;
|
||||
margin: 0 0 0.25em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -7,45 +7,10 @@
|
|||
<script src="./user_profile.js"></script>
|
||||
|
||||
<style lang="scss">
|
||||
.user-profile {
|
||||
flex: 2;
|
||||
flex-basis: 500px;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
.user-interactions {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: center;
|
||||
.user-profile {
|
||||
flex: 2;
|
||||
flex-basis: 500px;
|
||||
}
|
||||
|
||||
div {
|
||||
flex: 1;
|
||||
}
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: -1.2em;
|
||||
|
||||
.following {
|
||||
font-size: 14px;
|
||||
flex: 0 0 100%;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.mute {
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.follow {
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 80%;
|
||||
height: 100%;
|
||||
border: 1px solid;
|
||||
}
|
||||
}
|
||||
.user-screen-name {
|
||||
margin-top: 0.4em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import merge from 'lodash.merge'
|
||||
import objectPath from 'object-path'
|
||||
import { throttle } from 'lodash'
|
||||
import { inflate, deflate } from 'pako'
|
||||
import lzstring from 'lz-string'
|
||||
|
||||
const defaultReducer = (state, paths) => (
|
||||
paths.length === 0 ? state : paths.reduce((substate, path) => {
|
||||
|
@ -36,22 +36,22 @@ const defaultStorage = (() => {
|
|||
})()
|
||||
|
||||
const defaultSetState = (key, state, storage) => {
|
||||
return storage.setItem(key, deflate(JSON.stringify(state), { to: 'string' }))
|
||||
return storage.setItem(key, lzstring.compressToUTF16(JSON.stringify(state)))
|
||||
}
|
||||
|
||||
export default function createPersistedState ({
|
||||
key = 'vuex',
|
||||
key = 'vuex-lz',
|
||||
paths = [],
|
||||
getState = (key, storage) => {
|
||||
let value = storage.getItem(key)
|
||||
try {
|
||||
value = inflate(value, { to: 'string' })
|
||||
value = lzstring.decompressFromUTF16(value) // inflate(value, { to: 'string' })
|
||||
} catch (e) {
|
||||
console.log("Couldn't inflate value... Maybe upgrading")
|
||||
}
|
||||
return value && value !== 'undefined' ? JSON.parse(value) : undefined
|
||||
},
|
||||
setState = throttle(defaultSetState, 5000),
|
||||
setState = throttle(defaultSetState, 60000),
|
||||
reducer = defaultReducer,
|
||||
storage = defaultStorage,
|
||||
subscriber = store => handler => store.subscribe(handler)
|
||||
|
|
|
@ -29,7 +29,12 @@ Vue.use(VueTimeago, {
|
|||
})
|
||||
|
||||
const persistedStateOptions = {
|
||||
paths: ['users.users', 'statuses.notifications']
|
||||
paths: [
|
||||
'config.hideAttachments',
|
||||
'config.hideNsfw',
|
||||
'statuses.notifications',
|
||||
'users.users'
|
||||
]
|
||||
}
|
||||
|
||||
const store = new Vuex.Store({
|
||||
|
|
|
@ -2,7 +2,10 @@ import { set } from 'vue'
|
|||
import StyleSetter from '../services/style_setter/style_setter.js'
|
||||
|
||||
const defaultState = {
|
||||
name: 'Pleroma FE'
|
||||
name: 'Pleroma FE',
|
||||
colors: {},
|
||||
hideAttachments: false,
|
||||
hideNsfw: true
|
||||
}
|
||||
|
||||
const config = {
|
||||
|
@ -24,7 +27,7 @@ const config = {
|
|||
break
|
||||
case 'theme':
|
||||
const fullPath = `/static/css/${value}`
|
||||
StyleSetter.setStyle(fullPath)
|
||||
StyleSetter.setStyle(fullPath, commit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
const setStyle = (href) => {
|
||||
import { times } from 'lodash'
|
||||
|
||||
const setStyle = (href, commit) => {
|
||||
/***
|
||||
What's going on here?
|
||||
I want to make it easy for admins to style this application. To have
|
||||
|
@ -23,18 +25,26 @@ const setStyle = (href) => {
|
|||
const setDynamic = () => {
|
||||
const baseEl = document.createElement('div')
|
||||
body.appendChild(baseEl)
|
||||
baseEl.setAttribute('class', 'base05')
|
||||
const base05Color = window.getComputedStyle(baseEl).getPropertyValue('color')
|
||||
baseEl.setAttribute('class', 'base08')
|
||||
const base08Color = window.getComputedStyle(baseEl).getPropertyValue('color')
|
||||
|
||||
let colors = {}
|
||||
times(16, (n) => {
|
||||
const name = `base0${n.toString(16).toUpperCase()}`
|
||||
baseEl.setAttribute('class', name)
|
||||
const color = window.getComputedStyle(baseEl).getPropertyValue('color')
|
||||
colors[name] = color
|
||||
})
|
||||
|
||||
commit('setOption', { name: 'colors', value: colors })
|
||||
|
||||
body.removeChild(baseEl)
|
||||
|
||||
const styleEl = document.createElement('style')
|
||||
head.appendChild(styleEl)
|
||||
const styleSheet = styleEl.sheet
|
||||
body.removeChild(baseEl)
|
||||
|
||||
styleSheet.insertRule(`a { color: ${base08Color}`, 'index-max')
|
||||
styleSheet.insertRule(`body { color: ${base05Color}`, 'index-max')
|
||||
styleSheet.insertRule(`.base05-border { border-color: ${base05Color}`, 'index-max')
|
||||
styleSheet.insertRule(`a { color: ${colors['base08']}`, 'index-max')
|
||||
styleSheet.insertRule(`body { color: ${colors['base05']}`, 'index-max')
|
||||
styleSheet.insertRule(`.base05-border { border-color: ${colors['base05']}`, 'index-max')
|
||||
body.style.display = 'initial'
|
||||
}
|
||||
cssEl.addEventListener('load', setDynamic)
|
||||
|
|
14
yarn.lock
14
yarn.lock
|
@ -3549,6 +3549,10 @@ lru-cache@~2.6.5:
|
|||
version "2.6.5"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.6.5.tgz#e56d6354148ede8d7707b58d143220fd08df0fd5"
|
||||
|
||||
lz-string@^1.4.4:
|
||||
version "1.4.4"
|
||||
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"
|
||||
|
||||
macaddress@^0.2.8:
|
||||
version "0.2.8"
|
||||
resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12"
|
||||
|
@ -4052,10 +4056,6 @@ pac-resolver@~1.2.1:
|
|||
regenerator "~0.8.13"
|
||||
thunkify "~2.1.1"
|
||||
|
||||
pako@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.4.tgz#412cc97c3b7ff06dc6c2557fd4f03d06f5e708d4"
|
||||
|
||||
pako@~0.2.0:
|
||||
version "0.2.9"
|
||||
resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75"
|
||||
|
@ -5629,12 +5629,6 @@ vue-router@^2.2.0:
|
|||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-2.2.1.tgz#b027f9fac2cf13462725e843d6dc631b6aa077f6"
|
||||
|
||||
vue-style-loader@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-1.0.0.tgz#abeb7bd0f46313083741244d3079d4f14449e049"
|
||||
dependencies:
|
||||
loader-utils "^0.2.7"
|
||||
|
||||
vue-style-loader@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-2.0.0.tgz#1a3bb55239ac541ee3af0301d66f16fc86786543"
|
||||
|
|
Loading…
Reference in a new issue