Merge branch 'develop' into feature/hash-routed

This commit is contained in:
Roger Braun 2017-02-26 11:39:42 +01:00
commit 290180a987
23 changed files with 389 additions and 202 deletions

View file

@ -17,9 +17,9 @@
"babel-plugin-lodash": "^3.2.11", "babel-plugin-lodash": "^3.2.11",
"diff": "^3.0.1", "diff": "^3.0.1",
"karma-mocha-reporter": "^2.2.1", "karma-mocha-reporter": "^2.2.1",
"lz-string": "^1.4.4",
"node-sass": "^3.10.1", "node-sass": "^3.10.1",
"object-path": "^0.11.3", "object-path": "^0.11.3",
"pako": "^1.0.4",
"sanitize-html": "^1.13.0", "sanitize-html": "^1.13.0",
"sass-loader": "^4.0.2", "sass-loader": "^4.0.2",
"tributejs": "^2.1.0", "tributejs": "^2.1.0",
@ -84,7 +84,7 @@
"sinon-chai": "^2.8.0", "sinon-chai": "^2.8.0",
"url-loader": "^0.5.7", "url-loader": "^0.5.7",
"vue-loader": "^11.1.0", "vue-loader": "^11.1.0",
"vue-style-loader": "^1.0.0", "vue-style-loader": "^2.0.0",
"webpack": "^1.13.2", "webpack": "^1.13.2",
"webpack-dev-middleware": "^1.8.3", "webpack-dev-middleware": "^1.8.3",
"webpack-hot-middleware": "^2.12.2", "webpack-hot-middleware": "^2.12.2",

View file

@ -32,9 +32,10 @@ a {
button{ button{
border: none; border: none;
border-radius: 5px; border-radius: 5px;
cursor: pointer;
&:hover { &:hover {
background-color: white; opacity: 0.8;
} }
} }
@ -102,11 +103,11 @@ main-router {
flex-direction: column; flex-direction: column;
margin: 0.5em; margin: 0.5em;
border-radius: 0.5em; border-radius: 10px;
} }
.panel-heading { .panel-heading {
border-radius: 0.5em 0.5em 0 0; border-radius: 10px 10px 0 0;
background-size: cover; background-size: cover;
padding: 0.6em 0; padding: 0.6em 0;
text-align: center; text-align: center;
@ -115,7 +116,7 @@ main-router {
} }
.panel-footer { .panel-footer {
border-radius: 0 0 0.5em 0.5em; border-radius: 0 0 10px 10px;
} }
.panel-body > p { .panel-body > p {
@ -128,7 +129,7 @@ main-router {
#content { #content {
margin: auto; margin: auto;
max-width: 980px; max-width: 980px;
border-radius: 1em; border-radius: 10px;
padding-bottom: 1em; padding-bottom: 1em;
background-color: rgba(0,0,0,0.1); background-color: rgba(0,0,0,0.1);
} }
@ -142,46 +143,10 @@ main-router {
min-width: 0px; 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 { .fa {
color: grey; color: grey;
} }
.status-actions { .status-actions {
width: 50%; width: 50%;
display: flex; display: flex;
@ -210,7 +175,7 @@ status-text-container {
margin-top: 0.2em; margin-top: 0.2em;
float: right; float: right;
margin-right: 0.3em; margin-right: 0.3em;
border-radius: 20%; border-radius: 5px;
} }
} }

View file

@ -7,16 +7,19 @@ const Attachment = {
'nsfw', 'nsfw',
'statusId' 'statusId'
], ],
data: () => ({ data () {
nsfwImage, return {
showHidden: false nsfwImage,
}), hideNsfwLocal: this.$store.state.config.hideNsfw,
showHidden: false
}
},
computed: { computed: {
type () { type () {
return fileTypeService.fileType(this.attachment.mimetype) return fileTypeService.fileType(this.attachment.mimetype)
}, },
hidden () { hidden () {
return this.nsfw && !this.showHidden return this.nsfw && this.hideNsfwLocal && !this.showHidden
} }
}, },
methods: { methods: {

View file

@ -3,7 +3,7 @@
<a class="image-attachment" v-if="hidden" v-on:click.prevent="toggleHidden()"> <a class="image-attachment" v-if="hidden" v-on:click.prevent="toggleHidden()">
<img :key="nsfwImage" :src="nsfwImage"></img> <img :key="nsfwImage" :src="nsfwImage"></img>
</a> </a>
<div class="hider" v-if="nsfw && !hidden"> <div class="hider" v-if="nsfw && hideNsfwLocal && !hidden">
<a href="#" @click.prevent="toggleHidden()">Hide</a> <a href="#" @click.prevent="toggleHidden()">Hide</a>
</div> </div>
@ -16,8 +16,6 @@
<audio v-if="type === 'audio'" :src="attachment.url" controls></audio> <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 @click.prevent="linkClicked" v-if="type === 'html' && attachment.oembed" class="oembed">
<div v-if="attachment.thumb_url" class="image"> <div v-if="attachment.thumb_url" class="image">
<img :src="attachment.thumb_url"></img> <img :src="attachment.thumb_url"></img>
@ -36,14 +34,15 @@
.attachments { .attachments {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
margin-right: -0.8em;
.attachment { .attachment {
flex: 1 0 30%; flex: 1 0 30%;
display: flex; margin: 0.5em 0.8em 0.6em 0.0em;
margin: 0.5em 0.8em 0.6em 0.1em;
align-self: flex-start; align-self: flex-start;
&.html { &.html {
flex-basis: 100%; flex-basis: 100%;
display: flex;
} }
.hider { .hider {
@ -51,14 +50,14 @@
margin: 10px; margin: 10px;
padding: 5px; padding: 5px;
background: rgba(230,230,230,0.6); background: rgba(230,230,230,0.6);
border-radius: 0.5em; border-radius: 5px;
font-weight: bold; font-weight: bold;
} }
video { video {
height: 100%; height: 100%;
border: 1px solid; border: 1px solid;
border-radius: 0.5em; border-radius: 5px;
width: 100%; width: 100%;
} }
@ -71,7 +70,7 @@
height: 100%; height: 100%;
flex: 1; flex: 1;
border: 1px solid; border: 1px solid;
border-radius: 0.5em; border-radius: 5px;
} }
@ -91,7 +90,7 @@
flex: 1; flex: 1;
img { img {
border: 0px; border: 0px;
border-radius: 0; border-radius: 5px;
height: 100%; height: 100%;
object-fit: cover; object-fit: cover;
} }
@ -115,7 +114,7 @@
width: 100%; width: 100%;
border-style: solid; border-style: solid;
border-width: 1px; border-width: 1px;
border-radius: 0.5em; border-radius: 5px;
width: 100%; width: 100%;
height: 100%; /* If this isn't here, chrome will stretch the images */ height: 100%; /* If this isn't here, chrome will stretch the images */
} }

View file

@ -2,7 +2,7 @@ const DeleteButton = {
props: [ 'status' ], props: [ 'status' ],
methods: { methods: {
deleteStatus () { 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) { if (confirmed) {
this.$store.dispatch('deleteStatus', { id: this.status.id }) this.$store.dispatch('deleteStatus', { id: this.status.id })
} }
@ -10,7 +10,7 @@ const DeleteButton = {
}, },
computed: { computed: {
currentUser () { return this.$store.state.users.currentUser }, 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 }
} }
} }

View file

@ -1,11 +1,11 @@
<template> <template>
<div class="login panel panel-default base00-background"> <div class="login panel panel-default base00-background">
<!-- Default panel contents --> <!-- Default panel contents -->
<div class="panel-heading base01-background base04"> <div class="panel-heading base01-background">
Log in Log in
</div> </div>
<div class="panel-body"> <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'> <div class='form-group'>
<label for='username'>Username</label> <label for='username'>Username</label>
<input :disabled="loggingIn" v-model='user.username' class='form-control' id='username' placeholder='e.g. lain'> <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'> <input :disabled="loggingIn" v-model='user.password' class='form-control' id='password' type='password'>
</div> </div>
<div class='form-group'> <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> </div>
</form> </form>
</div> </div>
@ -23,3 +23,22 @@
</template> </template>
<script src="./login_form.js" ></script> <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>

View file

@ -3,12 +3,22 @@ import statusPosterService from '../../services/status_poster/status_poster.serv
const mediaUpload = { const mediaUpload = {
mounted () { mounted () {
const store = this.$store
const input = this.$el.querySelector('input') const input = this.$el.querySelector('input')
const self = this
input.addEventListener('change', ({target}) => { input.addEventListener('change', ({target}) => {
const file = target.files[0] 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() const formData = new FormData()
formData.append('media', file) formData.append('media', file)
@ -19,15 +29,34 @@ const mediaUpload = {
.then((fileData) => { .then((fileData) => {
self.$emit('uploaded', fileData) self.$emit('uploaded', fileData)
self.uploading = false self.uploading = false
}, (error) => { }, (error) => { // eslint-disable-line handle-callback-err
self.$emit('upload-failed') self.$emit('upload-failed')
self.uploading = false 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 () { props: [
return { 'dropFiles'
uploading: false ],
watch: {
'dropFiles': function (fileInfos) {
if (!this.uploading) {
this.uploadFile(fileInfos[0])
}
} }
} }
} }

View file

@ -1,5 +1,5 @@
<template> <template>
<div class="media-upload"> <div class="media-upload" @drop.prevent @dragover.prevent="fileDrag" @drop="fileDrop">
<label class="btn btn-default"> <label class="btn btn-default">
<i class="fa icon-spin4 animate-spin" v-if="uploading"></i> <i class="fa icon-spin4 animate-spin" v-if="uploading"></i>
<i class="fa icon-upload" v-if="!uploading"></i> <i class="fa icon-upload" v-if="!uploading"></i>

View file

@ -1,45 +1,65 @@
@import '../../_variables.scss'; @import '../../_variables.scss';
.notification {
padding: 0.4em 0 0 0.7em;
display: flex;
border-bottom: 1px solid silver;
.text { .notifications {
min-width: 0px;
word-wrap: break-word;
line-height:18px;
.icon-retweet { .panel-heading {
color: $green; // force the text to stay centered, while keeping
} // the button in the right side of the panel heading
position: relative;
.icon-reply { button {
color: $blue; position: absolute;
} padding: 0.1em 0.3em 0.25em 0.3em;
right: 0.6em;
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 { .unseen {
padding-top: 0.3em; border-left: 4px solid rgba(255, 48, 16, 0.65);
width: 32px;
height: 32px;
border-radius: 50%;
} }
&:last-child { .notification {
border: none 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
}
} }
} }

View file

@ -1,9 +1,12 @@
<template> <template>
<div class="notifications"> <div class="notifications">
<div class="panel panel-default base00-background"> <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 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"> <a :href="notification.action.user.statusnet_profile_url">
<img class='avatar' :src="notification.action.user.profile_image_url_original"> <img class='avatar' :src="notification.action.user.profile_image_url_original">
</a> </a>

View file

@ -49,7 +49,7 @@ const defaultCollection = {
menuContainer: document.body, menuContainer: document.body,
// column to search against in the object (accepts function or string) // 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 // column that contains the content to insert by default
fillAttr: 'screen_name', fillAttr: 'screen_name',
@ -84,6 +84,7 @@ const PostStatusForm = {
} }
return { return {
dropFiles: [],
submitDisabled: false, submitDisabled: false,
newStatus: { newStatus: {
status: statusText, status: statusText,
@ -141,6 +142,15 @@ const PostStatusForm = {
}, },
type (fileInfo) { type (fileInfo) {
return fileTypeService.fileType(fileInfo.mimetype) 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'
} }
} }
} }

View file

@ -2,20 +2,20 @@
<div class="post-status-form"> <div class="post-status-form">
<form @submit.prevent="postStatus(newStatus)"> <form @submit.prevent="postStatus(newStatus)">
<div class="form-group" > <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>
<div class="attachments"> <div class="attachments">
<div class="attachment" v-for="file in newStatus.files"> <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> <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> <video v-if="type(file) === 'video'" :src="file.image" controls></video>
<audio v-if="type(file) === 'audio'" :src="file.image" controls></audio> <audio v-if="type(file) === 'audio'" :src="file.image" controls></audio>
<a v-if="type(file) === 'unknown'" :href="file.image">{{file.url}}</a> <a v-if="type(file) === 'unknown'" :href="file.image">{{file.url}}</a>
<i class="fa icon-cancel" @click="removeMediaFile(file)"></i>
</div> </div>
</div> </div>
<div class='form-bottom'> <div class='form-bottom'>
<media-upload @uploading="disableSubmit" @uploaded="addMediaFile" @upload-failed="enableSubmit"></media-upload> <media-upload @uploading="disableSubmit" @uploaded="addMediaFile" @upload-failed="enableSubmit" :drop-files="dropFiles"></media-upload>
<button :disabled="submitDisabled" type="submit" class="btn btn-default">Submit</button> <button :disabled="submitDisabled" type="submit" class="btn btn-default base05 base01-background">Submit</button>
</div> </div>
</form> </form>
</div> </div>
@ -52,6 +52,15 @@
.attachments { .attachments {
padding: 0.5em; padding: 0.5em;
i {
position: absolute;
margin: 10px;
padding: 5px;
background: rgba(230,230,230,0.6);
border-radius: 5px;
font-weight: bold;
}
} }
form { form {
@ -68,10 +77,12 @@
} }
form textarea { form textarea {
border: none; border: solid;
border-radius: 2px; border-width: 1px;
border-color: silver;
border-radius: 5px;
line-height:16px; line-height:16px;
padding: 0.5em; padding: 5px;
resize: vertical; resize: vertical;
} }

View file

@ -1,8 +1,22 @@
import StyleSwitcher from '../style_switcher/style_switcher.vue' import StyleSwitcher from '../style_switcher/style_switcher.vue'
const settings = { const settings = {
data () {
return {
hideAttachmentsLocal: this.$store.state.config.hideAttachments,
hideNsfwLocal: this.$store.state.config.hideNsfw
}
},
components: { components: {
StyleSwitcher StyleSwitcher
},
watch: {
hideAttachmentsLocal (value) {
this.$store.dispatch('setOption', { name: 'hideAttachments', value })
},
hideNsfwLocal (value) {
this.$store.dispatch('setOption', { name: 'hideNsfw', value })
}
} }
} }

View file

@ -8,6 +8,13 @@
<h2>Theme</h2> <h2>Theme</h2>
<style-switcher></style-switcher> <style-switcher></style-switcher>
</div> </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>
</div> </div>
</template> </template>

View file

@ -17,6 +17,7 @@ const Status = {
userExpanded: false userExpanded: false
}), }),
computed: { computed: {
hideAttachments () { return this.$store.state.config.hideAttachments },
retweet () { return !!this.statusoid.retweeted_status }, retweet () { return !!this.statusoid.retweeted_status },
retweeter () { return this.statusoid.user.name }, retweeter () { return this.statusoid.user.name },
status () { status () {

View file

@ -57,7 +57,7 @@
<div @click.prevent="linkClicked" class="status-content" v-html="status.statusnet_html"></div> <div @click.prevent="linkClicked" class="status-content" v-html="status.statusnet_html"></div>
<div v-if='status.attachments' class='attachments'> <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> </attachment>
</div> </div>
</div> </div>
@ -157,7 +157,8 @@
.usercard { .usercard {
border-style: solid; border-style: solid;
border-width: 1px; border-width: 1px;
border-radius: 1em; border-radius: 10px;
margin-bottom: 1em; margin-bottom: 1em;
margin-top: 0.2em;
} }
</style> </style>

View file

@ -1,40 +1,42 @@
<template> <template>
<div> <div id="heading" class="profile-panel-background" :style="headingStyle">
<div class="base00-background panel-heading text-center" v-bind:style="style"> <div class="panel-heading text-center">
<div class='user-info'> <div class='user-info'>
<img :src="user.profile_image_url"> <div class='container'>
<span class="glyphicon glyphicon-user"></span> <img :src="user.profile_image_url">
<div class='user-name'>{{user.name}}</div> <span class="glyphicon glyphicon-user"></span>
<div class='user-screen-name'>@{{user.screen_name}}</div> <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="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! Follows you!
</div> </div>
<div class="follow"> <div class="follow" v-if="loggedIn">
<span v-if="user.following"> <span v-if="user.following">
<!--Following them!--> <!--Following them!-->
<button @click="unfollowUser" class="base06 base01-background base06-border"> <button @click="unfollowUser" class="base04 base00-background pressed">
Unfollow Following!
</button> </button>
</span> </span>
<span v-if="!user.following"> <span v-if="!user.following">
<button @click="followUser" class="base01 base04-background base01-border"> <button @click="followUser" class="base05 base02-background">
Follow Follow
</button> </button>
</span> </span>
</div> </div>
<div class='mute' v-if='isOtherUser'> <div class='mute' v-if='isOtherUser'>
<span v-if='user.muted'> <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>
<span v-if='!user.muted'> <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> </span>
</div> </div>
</div> </div>
</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-counts">
<div class="user-count"> <div class="user-count">
<h5>Statuses</h5> <h5>Statuses</h5>
@ -58,14 +60,25 @@
export default { export default {
props: [ 'user' ], props: [ 'user' ],
computed: { computed: {
style () { headingStyle () {
let rgb = this.$store.state.config.colors['base00'].match(/\d+/g)
return { return {
color: `#${this.user.profile_link_color}`, backgroundColor: 'rgb(' + Math.floor(rgb[0] * 0.53) + ', ' +
'background-image': `url(${this.user.cover_photo})` 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 () { isOtherUser () {
return this.user !== this.$store.state.users.currentUser return this.user !== this.$store.state.users.currentUser
},
loggedIn () {
return this.$store.state.users.currentUser
} }
}, },
methods: { methods: {
@ -87,3 +100,118 @@
} }
} }
</script> </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>

View file

@ -7,45 +7,10 @@
<script src="./user_profile.js"></script> <script src="./user_profile.js"></script>
<style lang="scss"> <style lang="scss">
.user-profile {
flex: 2;
flex-basis: 500px;
}
.user-info { .user-profile {
.user-interactions { flex: 2;
display: flex; flex-basis: 500px;
flex-flow: row wrap; }
justify-content: center;
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> </style>

View file

@ -1,7 +1,7 @@
import merge from 'lodash.merge' import merge from 'lodash.merge'
import objectPath from 'object-path' import objectPath from 'object-path'
import { throttle } from 'lodash' import { throttle } from 'lodash'
import { inflate, deflate } from 'pako' import lzstring from 'lz-string'
const defaultReducer = (state, paths) => ( const defaultReducer = (state, paths) => (
paths.length === 0 ? state : paths.reduce((substate, path) => { paths.length === 0 ? state : paths.reduce((substate, path) => {
@ -36,22 +36,22 @@ const defaultStorage = (() => {
})() })()
const defaultSetState = (key, state, storage) => { 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 ({ export default function createPersistedState ({
key = 'vuex', key = 'vuex-lz',
paths = [], paths = [],
getState = (key, storage) => { getState = (key, storage) => {
let value = storage.getItem(key) let value = storage.getItem(key)
try { try {
value = inflate(value, { to: 'string' }) value = lzstring.decompressFromUTF16(value) // inflate(value, { to: 'string' })
} catch (e) { } catch (e) {
console.log("Couldn't inflate value... Maybe upgrading") console.log("Couldn't inflate value... Maybe upgrading")
} }
return value && value !== 'undefined' ? JSON.parse(value) : undefined return value && value !== 'undefined' ? JSON.parse(value) : undefined
}, },
setState = throttle(defaultSetState, 5000), setState = throttle(defaultSetState, 60000),
reducer = defaultReducer, reducer = defaultReducer,
storage = defaultStorage, storage = defaultStorage,
subscriber = store => handler => store.subscribe(handler) subscriber = store => handler => store.subscribe(handler)

View file

@ -29,7 +29,12 @@ Vue.use(VueTimeago, {
}) })
const persistedStateOptions = { const persistedStateOptions = {
paths: ['users.users', 'statuses.notifications'] paths: [
'config.hideAttachments',
'config.hideNsfw',
'statuses.notifications',
'users.users'
]
} }
const store = new Vuex.Store({ const store = new Vuex.Store({

View file

@ -2,7 +2,10 @@ import { set } from 'vue'
import StyleSetter from '../services/style_setter/style_setter.js' import StyleSetter from '../services/style_setter/style_setter.js'
const defaultState = { const defaultState = {
name: 'Pleroma FE' name: 'Pleroma FE',
colors: {},
hideAttachments: false,
hideNsfw: true
} }
const config = { const config = {
@ -24,7 +27,7 @@ const config = {
break break
case 'theme': case 'theme':
const fullPath = `/static/css/${value}` const fullPath = `/static/css/${value}`
StyleSetter.setStyle(fullPath) StyleSetter.setStyle(fullPath, commit)
} }
} }
} }

View file

@ -1,4 +1,6 @@
const setStyle = (href) => { import { times } from 'lodash'
const setStyle = (href, commit) => {
/*** /***
What's going on here? What's going on here?
I want to make it easy for admins to style this application. To have I want to make it easy for admins to style this application. To have
@ -23,18 +25,26 @@ const setStyle = (href) => {
const setDynamic = () => { const setDynamic = () => {
const baseEl = document.createElement('div') const baseEl = document.createElement('div')
body.appendChild(baseEl) body.appendChild(baseEl)
baseEl.setAttribute('class', 'base05')
const base05Color = window.getComputedStyle(baseEl).getPropertyValue('color') let colors = {}
baseEl.setAttribute('class', 'base08') times(16, (n) => {
const base08Color = window.getComputedStyle(baseEl).getPropertyValue('color') 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') const styleEl = document.createElement('style')
head.appendChild(styleEl) head.appendChild(styleEl)
const styleSheet = styleEl.sheet const styleSheet = styleEl.sheet
body.removeChild(baseEl)
styleSheet.insertRule(`a { color: ${base08Color}`, 'index-max') styleSheet.insertRule(`a { color: ${colors['base08']}`, 'index-max')
styleSheet.insertRule(`body { color: ${base05Color}`, 'index-max') styleSheet.insertRule(`body { color: ${colors['base05']}`, 'index-max')
styleSheet.insertRule(`.base05-border { border-color: ${base05Color}`, 'index-max') styleSheet.insertRule(`.base05-border { border-color: ${colors['base05']}`, 'index-max')
body.style.display = 'initial' body.style.display = 'initial'
} }
cssEl.addEventListener('load', setDynamic) cssEl.addEventListener('load', setDynamic)

View file

@ -3549,6 +3549,10 @@ lru-cache@~2.6.5:
version "2.6.5" version "2.6.5"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.6.5.tgz#e56d6354148ede8d7707b58d143220fd08df0fd5" 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: macaddress@^0.2.8:
version "0.2.8" version "0.2.8"
resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12" 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" regenerator "~0.8.13"
thunkify "~2.1.1" 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: pako@~0.2.0:
version "0.2.9" version "0.2.9"
resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" 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" version "2.2.1"
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-2.2.1.tgz#b027f9fac2cf13462725e843d6dc631b6aa077f6" 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: vue-style-loader@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-2.0.0.tgz#1a3bb55239ac541ee3af0301d66f16fc86786543" resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-2.0.0.tgz#1a3bb55239ac541ee3af0301d66f16fc86786543"