Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85826395
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Authored By
Unknown
Size
87 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/App.scss b/src/App.scss
index ec80626bc3..a7b565237f 100644
--- a/src/App.scss
+++ b/src/App.scss
@@ -1,363 +1,367 @@
@import './_variables.scss';
#app {
background-size: cover;
background-attachment: fixed;
background-repeat: no-repeat;
background-position: 0 50px;
min-height: 100vh;
}
i {
user-select: none;
}
h4 {
margin: 0;
}
#content {
box-sizing: border-box;
padding-top: 60px;
margin: auto;
min-height: 100vh;
max-width: 980px;
background-color: rgba(0,0,0,0.15);
align-content: flex-start;
}
.text-center {
text-align: center;
}
body {
font-family: sans-serif;
font-size: 14px;
margin: 0;
color: $fallback--fg;
color: var(--fg, $fallback--fg);
}
a {
text-decoration: none;
color: $fallback--link;
color: var(--link, $fallback--link);
}
button{
user-select: none;
color: $fallback--faint;
color: var(--faint, $fallback--faint);
background-color: $fallback--btn;
background-color: var(--btn, $fallback--btn);
border: none;
- border-radius: 5px;
+ border-radius: $fallback--btnRadius;
+ border-radius: var(--btnRadius, $fallback--btnRadius);
cursor: pointer;
border-top: 1px solid rgba(255, 255, 255, 0.2);
border-bottom: 1px solid rgba(0, 0, 0, 0.2);
box-shadow: 0px 0px 2px black;
font-size: 14px;
font-family: sans-serif;
&:hover {
box-shadow: 0px 0px 4px rgba(255, 255, 255, 0.3);
}
&:disabled {
cursor: not-allowed;
opacity: 0.5;
}
&.pressed {
color: $fallback--faint;
color: var(--faint, $fallback--faint);
background-color: $fallback--bg;
background-color: var(--bg, $fallback--bg)
}
}
input, textarea, select {
border: none;
- border-radius: 5px;
+ border-radius: $fallback--btnRadius;
+ border-radius: var(--btnRadius, $fallback--btnRadius);
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
border-top: 1px solid rgba(0, 0, 0, 0.2);
box-shadow: 0px 0px 2px black inset;
background-color: $fallback--lightBg;
background-color: var(--lightBg, $fallback--lightBg);
color: $fallback--lightFg;
color: var(--lightFg, $fallback--lightFg);
font-family: sans-serif;
font-size: 14px;
padding: 8px 7px 4px;
+ box-sizing: border-box;
// TODO: Restyle <select> in a decent way. Needs different markup
// -webkit-appearance:none;
// -moz-appearance:none;
// appearance:none;
&[type=radio],
&[type=checkbox] {
display: none;
&:checked + label::before {
color: $fallback--fg;
color: var(--fg, $fallback--fg);
}
+ label::before {
display: inline-block;
content: '✔';
transition: color 200ms;
width: 1.1em;
height: 1.1em;
- border-radius: 2px;
+ border-radius: $fallback--checkBoxRadius;
+ border-radius: var(--checkBoxRadius, $fallback--checkBoxRadius);
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
border-top: 1px solid rgba(0, 0, 0, 0.2);
box-shadow: 0px 0px 2px black inset;
margin-right: .5em;
background-color: $fallback--btn;
background-color: var(--btn, $fallback--btn);
- vertical-align: baseline;
+ vertical-align: top;
text-align: center;
line-height: 1.1em;
font-size: 1.1em;
box-sizing: border-box;
color: transparent;
overflow: hidden;
+ box-sizing: border-box;
}
}
}
-.icon-cancel,
-.icon-reply,
-.icon-retweet,
-.icon-star-empty,
-.icon-eye-off,
-.icon-binoculars,
-.icon-plus-squared,
-.icon-spin4 {
+i[class^=icon-] {
color: $fallback--icon;
color: var(--icon, $fallback--icon)
}
.container {
display: flex;
flex-wrap: wrap;
margin: 0;
padding: 0 10px 0 10px;
}
.gaps {
margin: -1em 0 0 -1em;
}
.item {
flex: 1;
line-height: 25px;
height: 25px;
overflow: hidden;
.nav-icon {
font-size: 1.1em;
margin-left: 0.4em;
}
}
.gaps > .item {
padding: 1em 0 0 1em;
}
.auto-size {
flex: 1
}
nav {
width: 100%;
align-items: center;
position: fixed;
height: 50px;
.inner-nav {
padding-left: 20px;
padding-right: 20px;
display: flex;
align-items: center;
flex-basis: 970px;
margin: auto;
height: 50px;
background-repeat: no-repeat;
background-position: center;
background-size: contain;
+
+ a i {
+ color: $fallback--link;
+ color: var(--link, $fallback--link);
+ }
}
}
main-router {
flex: 1;
}
.status.compact {
color: rgba(0, 0, 0, 0.42);
font-weight: 300;
p {
margin: 0;
font-size: 0.8em
}
}
/* Panel */
.panel {
display: flex;
flex-direction: column;
margin: 0.5em;
background-color: $fallback--bg;
background-color: var(--bg, $fallback--bg);
- border-radius: 10px;
border-radius: $fallback--panelRadius;
- border-radius: var(--panelRadius, 10px);
+ border-radius: var(--panelRadius, $fallback--panelRadius);
box-shadow: 1px 1px 4px rgba(0,0,0,.6);
overflow: hidden;
}
.panel-body:empty::before {
content: "¯\\_(ツ)_/¯"; // Could use words but it'd require translations
display: block;
margin: 1em;
text-align: center;
}
.panel-heading {
- border-radius: 10px 10px 0 0;
+ border-radius: $fallback--panelRadius $fallback--panelRadius 0 0;
+ border-radius: var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius) 0 0;
background-size: cover;
padding: 0.6em 1.0em;
text-align: left;
font-size: 1.3em;
line-height: 24px;
background-color: $fallback--btn;
background-color: var(--btn, $fallback--btn);
}
.panel-footer {
- border-radius: 0 0 10px 10px;
+ border-radius: 0 0 $fallback--panelRadius $fallback--panelRadius;
+ border-radius: 0 0 var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius);
}
.panel-body > p {
line-height: 18px;
padding: 1em;
margin: 0;
}
.container > * {
min-width: 0px;
}
.fa {
color: grey;
}
nav {
z-index: 1000;
background-color: $fallback--btn;
background-color: var(--btn, $fallback--btn);
color: $fallback--faint;
color: var(--faint, $fallback--faint);
box-shadow: 0px 0px 4px rgba(0,0,0,.6);
}
.fade-enter-active, .fade-leave-active {
transition: opacity .2s
}
.fade-enter, .fade-leave-active {
opacity: 0
}
.main {
flex-basis: 60%;
flex-grow: 1;
flex-shrink: 1;
}
.sidebar-bounds {
flex: 0;
flex-basis: 35%;
}
.sidebar-flexer {
flex: 1;
flex-basis: 345px;
width: 365px;
}
.mobile-shown {
display: none;
}
.panel-switcher {
display: none;
width: 100%;
height: 46px;
button {
display: block;
flex: 1;
max-height: 32px;
margin: 0.5em;
padding: 0.5em;
}
}
@media all and (min-width: 960px) {
body {
overflow-y: scroll;
}
.sidebar-bounds {
overflow: hidden;
max-height: 100vh;
width: 345px;
position: fixed;
margin-top: -10px;
.sidebar-scroller {
height: 96vh;
width: 365px;
padding-top: 10px;
padding-right: 50px;
overflow-x: hidden;
overflow-y: scroll;
}
.sidebar {
width: 345px;
}
}
.sidebar-flexer {
max-height: 96vh;
flex-shrink: 0;
flex-grow: 0;
}
}
@media all and (max-width: 959px) {
.mobile-hidden {
display: none;
}
.panel-switcher {
display: flex;
}
.container {
padding: 0 0 0 0;
}
.panel {
margin: 0.5em 0 0.5em 0;
}
}
.item.right {
text-align: right;
padding-right: 20px;
}
diff --git a/src/_variables.scss b/src/_variables.scss
index eccc0aabf0..1f5d4b3e47 100644
--- a/src/_variables.scss
+++ b/src/_variables.scss
@@ -1,20 +1,25 @@
$main-color: #f58d2c;
$main-background: white;
$darkened-background: whitesmoke;
$fallback--bg: #121a24;
$fallback--btn: #182230;
$fallback--faint: #999;
$fallback--fg: #b9b9ba;
$fallback--link: #d8a070;
$fallback--icon: #666;
$fallback--lightBg: rgb(21, 30, 42);
$fallback--lightFg: #b9b9ba;
$fallback--border: #222;
$fallback--cRed: #ff0000;
$fallback--cBlue: #0095ff;
$fallback--cGreen: #0fa00f;
$fallback--cOrange: orange;
-$fallback--cYellow: yellow;
$fallback--panelRadius: 10px;
+$fallback--checkBoxRadius: 2px;
+$fallback--btnRadius: 4px;
+$fallback--tooltipRadius: 5px;
+$fallback--avatarRadius: 4px;
+$fallback--avatarAltRadius: 10px;
+$fallback--attachmentRadius: 10px;
diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue
index e3b1e4d41b..1fdee5d3f2 100644
--- a/src/components/attachment/attachment.vue
+++ b/src/components/attachment/attachment.vue
@@ -1,143 +1,145 @@
<template>
<div class="attachment" :class="{[type]: true, loading}" v-show="!isEmpty">
<a class="image-attachment" v-if="hidden" @click.prevent="toggleHidden()">
<img :key="nsfwImage" :src="nsfwImage"/>
</a>
<div class="hider" v-if="nsfw && hideNsfwLocal && !hidden">
<a href="#" @click.prevent="toggleHidden()">Hide</a>
</div>
<a v-if="type === 'image' && !hidden" class="image-attachment" :href="attachment.url" target="_blank">
<StillImage referrerpolicy="no-referrer" :mimetype="attachment.mimetype" :src="attachment.large_thumb_url || attachment.url"/>
</a>
<video v-if="type === 'video' && !hidden" :src="attachment.url" controls loop></video>
<audio v-if="type === 'audio'" :src="attachment.url" controls></audio>
<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"/>
</div>
<div class="text">
<h1><a :href="attachment.url">{{attachment.oembed.title}}</a></h1>
<div v-html="attachment.oembed.oembedHTML"></div>
</div>
</div>
</div>
</template>
<script src="./attachment.js"></script>
<style lang="scss">
@import '../../_variables.scss';
.attachments {
display: flex;
flex-wrap: wrap;
margin-right: -0.7em;
.attachment.media-upload-container {
flex: 0 0 auto;
max-height: 300px;
max-width: 100%;
}
.attachment {
flex: 1 0 30%;
margin: 0.5em 0.7em 0.6em 0.0em;
align-self: flex-start;
border-color: $fallback--border;
border-color: var(--border, $fallback--border);
border-style: solid;
border-width: 1px;
- border-radius: 5px;
+ border-radius: $fallback--attachmentRadius;
+ border-radius: var(--attachmentRadius, $fallback--attachmentRadius);
overflow: hidden;
// fixes small gap below video
&.video {
line-height: 0;
}
&.html {
flex-basis: 90%;
width: 100%;
display: flex;
}
&.loading {
cursor: progress;
}
.hider {
position: absolute;
margin: 10px;
padding: 5px;
background: rgba(230,230,230,0.6);
font-weight: bold;
z-index: 4;
}
video {
max-height: 500px;
height: 100%;
width: 100%;
z-index: 0;
}
audio {
width: 100%;
}
img.media-upload {
margin-bottom: -2px;
max-height: 300px;
max-width: 100%;
}
.oembed {
width: 100%;
margin-right: 15px;
display: flex;
img {
width: 100%;
}
.image {
flex: 1;
img {
border: 0px;
- border-radius: 5px;
+ border-radius: $fallback--attachmentRadius;
+ border-radius: var(--attachmentRadius, $fallback--attachmentRadius);
height: 100%;
object-fit: cover;
}
}
.text {
flex: 2;
margin: 8px;
word-break: break-all;
h1 {
font-size: 14px;
margin: 0px;
}
}
}
a.image-attachment {
display: flex;
flex: 1;
img {
object-fit: contain;
width: 100%;
height: 100%; /* If this isn't here, chrome will stretch the images */
max-height: 500px;
image-orientation: from-image;
}
}
}
}
</style>
diff --git a/src/components/chat_panel/chat_panel.vue b/src/components/chat_panel/chat_panel.vue
index 69996f76dc..fd997aa828 100644
--- a/src/components/chat_panel/chat_panel.vue
+++ b/src/components/chat_panel/chat_panel.vue
@@ -1,58 +1,64 @@
<template>
<div class="chat-panel">
<div class="panel panel-default">
<div class="panel-heading timeline-heading">
<div class="title">
{{$t('chat.title')}}
</div>
</div>
<div class="chat-window" v-chat-scroll>
<div class="chat-message" v-for="message in messages" :key="message.id">
<span class="chat-avatar">
<img :src="message.author.avatar" />
{{message.author.username}}:
</span>
<span class="chat-text">
{{message.text}}
</span>
</div>
</div>
<div class="chat-input">
<form @submit.prevent="submit(currentMessage)">
<input v-model="currentMessage" type="text" >
</form>
</div>
</div>
</div>
</template>
<script src="./chat_panel.js"></script>
<style lang="scss">
- .chat-window {
- max-height: 200px;
- overflow-y: auto;
- overflow-x: hidden;
- }
- .chat-message {
- padding: 0.2em 0.5em
- }
- .chat-avatar {
- img {
- height: 32px;
- width: 32px;
- border-radius: 5px;
- margin-right: 0.5em;
- }
- }
- .chat-input {
- display: flex;
- form {
- flex: auto;
- input {
- margin: 0.5em;
- width: fill-available;
- }
- }
- }
+@import '../../_variables.scss';
+
+.chat-window {
+ max-height: 200px;
+ overflow-y: auto;
+ overflow-x: hidden;
+}
+
+.chat-message {
+ padding: 0.2em 0.5em
+}
+
+.chat-avatar {
+ img {
+ height: 32px;
+ width: 32px;
+ border-radius: $fallback--avatarRadius;
+ border-radius: var(--avatarRadius, $fallback--avatarRadius);
+ margin-right: 0.5em;
+ }
+}
+
+.chat-input {
+ display: flex;
+ form {
+ flex: auto;
+ input {
+ margin: 0.5em;
+ width: fill-available;
+ }
+ }
+}
</style>
diff --git a/src/components/login_form/login_form.vue b/src/components/login_form/login_form.vue
index 82000d6b06..ac492262f3 100644
--- a/src/components/login_form/login_form.vue
+++ b/src/components/login_form/login_form.vue
@@ -1,69 +1,65 @@
<template>
<div class="login panel panel-default">
<!-- Default panel contents -->
<div class="panel-heading">
{{$t('login.login')}}
</div>
<div class="panel-body">
<form v-on:submit.prevent='submit(user)' class='login-form'>
<div class='form-group'>
<label for='username'>{{$t('login.username')}}</label>
<input :disabled="loggingIn" v-model='user.username' class='form-control' id='username' placeholder='e.g. lain'>
</div>
<div class='form-group'>
<label for='password'>{{$t('login.password')}}</label>
<input :disabled="loggingIn" v-model='user.password' class='form-control' id='password' type='password'>
</div>
<div class='form-group'>
<div class='login-bottom'>
<div><router-link :to="{name: 'registration'}" v-if='registrationOpen' class='register'>{{$t('login.register')}}</router-link></div>
<button :disabled="loggingIn" type='submit' class='btn btn-default'>{{$t('login.login')}}</button>
</div>
</div>
<div v-if="authError" class='form-group'>
<div class='error'>{{authError}}</div>
</div>
</form>
</div>
</div>
</template>
<script src="./login_form.js" ></script>
<style lang="scss">
@import '../../_variables.scss';
.login-form {
- input {
- border-radius: 5px;
- padding: 0.1em 0.2em 0.2em 0.2em;
- }
-
.btn {
min-height: 28px;
width: 10em;
}
.error {
- border-radius: 5px;
+ border-radius: $fallback--tooltipRadius;
+ border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
text-align: center;
background-color: rgba(255, 48, 16, 0.65);
- background-color: $fallback--cRed;
+ background-color: $fallback--cRed;
min-height: 28px;
line-height: 28px;
}
.register {
flex: 1 1;
}
.login-bottom {
margin-top: 1.0em;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
}
</style>
diff --git a/src/components/media_upload/media_upload.vue b/src/components/media_upload/media_upload.vue
index 8e83e7f1aa..8b931d2df3 100644
--- a/src/components/media_upload/media_upload.vue
+++ b/src/components/media_upload/media_upload.vue
@@ -1,22 +1,22 @@
<template>
<div class="media-upload" @drop.prevent @dragover.prevent="fileDrag" @drop="fileDrop">
<label class="btn btn-default">
- <i class="animate-spin" v-if="uploading"></i>
+ <i class="icon-spin4 animate-spin" v-if="uploading"></i>
<i class="icon-upload" v-if="!uploading"></i>
<input type=file style="position: fixed; top: -100em"></input>
</label>
</div>
</template>
<script src="./media_upload.js" ></script>
<style>
.media-upload {
font-size: 26px;
flex: 1;
}
.icon-upload {
cursor: pointer;
}
</style>
diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss
index 5af2454e29..7ef4f07d3d 100644
--- a/src/components/notifications/notifications.scss
+++ b/src/components/notifications/notifications.scss
@@ -1,170 +1,170 @@
@import '../../_variables.scss';
.notifications {
// a bit of a hack to allow scrolling below notifications
padding-bottom: 15em;
.panel {
background: $fallback--bg;
background: var(--bg, $fallback--bg)
}
.panel-body {
border-color: $fallback--border;
border-color: var(--border, $fallback--border)
}
.panel-heading {
// force the text to stay centered, while keeping
// the button in the right side of the panel heading
position: relative;
background: $fallback--btn;
background: var(--btn, $fallback--btn);
color: $fallback--faint;
color: var(--faint, $fallback--faint);
.read-button {
position: absolute;
right: 0.7em;
height: 1.8em;
line-height: 100%;
background-color: $fallback--btn;
background-color: var(--btn, $fallback--btn);
color: $fallback--faint;
color: var(--faint, $fallback--faint);
}
}
.unseen-count {
display: inline-block;
background-color: $fallback--cRed;
background-color: var(--cRed, $fallback--cRed);
text-shadow: 0px 0px 3px rgba(0, 0, 0, 0.5);
min-width: 1.3em;
border-radius: 1.3em;
margin: 0 0.2em 0 -0.4em;
color: white;
font-size: 0.9em;
text-align: center;
line-height: 1.3em;
padding: 1px;
}
.notification {
// Will have to use pixels here to ensure consistent distance with
// pad alone and pad + border, browsers bad at rounding this with em,
// they love to give a 1 pixel ghost offset with 0.7em vs 0.3em + 0.4em,
// which does not happen with 10px vs 4px + 6px.
padding: 0.4em 0 0 10px;
display: flex;
border-bottom: 1px solid;
border-bottom-color: inherit;
.notification-gradient {
background: linear-gradient(to bottom, rgba(0, 0, 0, 0), $fallback--bg 80%);
background: linear-gradient(to bottom, rgba(0, 0, 0, 0), var(--bg, $fallback--bg) 80%)
}
.text {
min-width: 0px;
word-wrap: break-word;
line-height:18px;
position: relative;
overflow: hidden;
.icon-retweet.lit {
color: $fallback--cGreen;
color: var(--cGreen, $fallback--cGreen);
}
.icon-user-plus.lit {
color: $fallback--cBlue;
color: var(--cBlue, $fallback--cBlue);
}
.icon-reply.lit {
color: $fallback--cBlue;
color: var(--cBlue, $fallback--cBlue);
}
.icon-star.lit {
color: orange;
color: $fallback--cOrange;
color: var(--cOrange, $fallback--cOrange);
}
.status-content {
margin: 0;
max-height: 300px;
}
h1 {
word-break: break-all;
margin: 0 0 0.3em;
padding: 0;
font-size: 1em;
line-height:20px;
small {
font-weight: lighter;
}
}
padding: 0.3em 0.8em 0.5em;
p {
margin: 0;
margin-top: 0;
margin-bottom: 0.3em;
}
}
.avatar {
margin-top: 0.3em;
width: 32px;
height: 32px;
+ border-radius: $fallback--avatarAltRadius;
border-radius: 50%;
overflow: hidden;
line-height: 0;
&.animated::before {
display: none;
}
}
&:hover .animated.avatar {
canvas {
display: none;
}
img {
visibility: visible;
}
}
&:last-child {
border-bottom: none;
- border-radius: 0 0 10px 10px;
}
}
.notification-content {
max-height: 12em;
overflow-y: hidden;
//text-overflow: ellipsis;
img {
object-fit: contain;
}
}
.notification-gradient {
position: absolute;
width: 100%;
height: 4em;
margin-top:8em;
}
.unseen {
border-left: 4px solid $fallback--cRed;
border-left: 4px solid var(--cRed, $fallback--cRed);
padding-left: 6px;
}
}
diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue
index d57418fbf3..abf3b5b7df 100644
--- a/src/components/post_status_form/post_status_form.vue
+++ b/src/components/post_status_form/post_status_form.vue
@@ -1,209 +1,215 @@
<template>
<div class="post-status-form">
<form @submit.prevent="postStatus(newStatus)">
<div class="form-group" >
<textarea @click="setCaret" @keyup="setCaret" v-model="newStatus.status" :placeholder="$t('post_status.default')" rows="1" class="form-control" @keydown.down="cycleForward" @keydown.up="cycleBackward" @keydown.shift.tab="cycleBackward" @keydown.tab="cycleForward" @keydown.enter="replaceCandidate" @keydown.meta.enter="postStatus(newStatus)" @keyup.ctrl.enter="postStatus(newStatus)" @drop="fileDrop" @dragover.prevent="fileDrag" @input="resize" @paste="paste"></textarea>
</div>
<div style="position:relative;" v-if="candidates">
<div class="autocomplete-panel">
<div v-for="candidate in candidates" @click="replace(candidate.utf || (candidate.screen_name + ' '))">
<div v-if="candidate.highlighted" class="autocomplete">
<span v-if="candidate.img"><img :src="candidate.img"></span>
<span v-else>{{candidate.utf}}</span>
<span>{{candidate.screen_name}}<small>{{candidate.name}}</small></span>
</div>
<div v-else class="autocomplete">
<span v-if="candidate.img"><img :src="candidate.img"></img></span>
<span v-else>{{candidate.utf}}</span>
<span>{{candidate.screen_name}}<small>{{candidate.name}}</small></span>
</div>
</div>
</div>
</div>
<div class='form-bottom'>
<media-upload @uploading="disableSubmit" @uploaded="addMediaFile" @upload-failed="enableSubmit" :drop-files="dropFiles"></media-upload>
<p v-if="isOverLengthLimit" class="error">{{ charactersLeft }}</p>
<p v-else-if="hasStatusLengthLimit">{{ charactersLeft }}</p>
<button v-if="posting" disabled class="btn btn-default">{{$t('post_status.posting')}}</button>
<button v-else-if="isOverLengthLimit" disabled class="btn btn-default">{{$t('general.submit')}}</button>
<button v-else :disabled="submitDisabled" type="submit" class="btn btn-default">{{$t('general.submit')}}</button>
</div>
<div class='error' v-if="error">
Error: {{ error }}
<i class="icon-cancel" @click="clearError"></i>
</div>
<div class="attachments">
<div class="media-upload-container 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>
</div>
</div>
</form>
</div>
</template>
<script src="./post_status_form.js"></script>
<style lang="scss">
@import '../../_variables.scss';
.tribute-container {
ul {
padding: 0px;
li {
display: flex;
align-items: center;
}
}
img {
padding: 3px;
width: 16px;
height: 16px;
- border-radius: 50%;
+ border-radius: $fallback--avatarAltRadius;
+ border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius);
}
}
.post-status-form, .login {
.form-bottom {
display: flex;
padding: 0.5em;
height: 32px;
button {
width: 10em;
}
p {
margin: 0.35em;
padding: 0.35em;
display: flex;
}
}
.error {
- border-radius: 5px;
+ border-radius: $fallback--tooltipRadius;
+ border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
text-align: center;
background-color: rgba(255, 48, 16, 0.65);
background-color: $fallback--cRed;
background-color: var(--cRed, $fallback--cRed);
padding: 0.25em;
margin: 0.35em;
display: flex;
}
.attachments {
padding: 0 0.5em;
.attachment {
position: relative;
border: $fallback--border;
border: var(--border, $fallback--border);
margin: 0.5em 0.8em 0.2em 0;
}
i {
position: absolute;
margin: 10px;
padding: 5px;
background: rgba(230,230,230,0.6);
- border-radius: 5px;
+ border-radius: $fallback--attachmentRadius;
+ border-radius: var(--attachmentRadius, $fallback--attachmentRadius);
font-weight: bold;
}
}
.btn {
cursor: pointer;
}
.btn[disabled] {
cursor: not-allowed;
}
.icon-cancel {
cursor: pointer;
}
form {
display: flex;
flex-direction: column;
padding: 0.6em;
}
.form-group {
display: flex;
flex-direction: column;
padding: 0.3em 0.5em 0.6em;
line-height:24px;
}
form textarea {
line-height:16px;
resize: none;
overflow: hidden;
transition: min-height 200ms;
min-height: 1px;
+ box-sizing: content-box;
}
form textarea:focus {
min-height: 48px;
}
.btn {
cursor: pointer;
}
.btn[disabled] {
cursor: not-allowed;
}
.icon-cancel {
cursor: pointer;
z-index: 4;
}
.autocomplete-panel {
margin: 0 0.5em 0 0.5em;
- border-radius: 5px;
+ border-radius: $fallback--tooltipRadius;
+ border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
position: absolute;
z-index: 1;
box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.5);
min-width: 75%;
background: $fallback--btn;
background: var(--btn, $fallback--btn);
color: $fallback--lightFg;
color: var(--lightFg, $fallback--lightFg);
}
.autocomplete {
cursor: pointer;
padding: 0.2em 0.4em 0.2em 0.4em;
border-bottom: 1px solid rgba(0, 0, 0, 0.4);
display: flex;
img {
width: 24px;
height: 24px;
- border-radius: 2px;
+ border-radius: $fallback--avatarRadius;
+ border-radius: var(--avatarRadius, $fallback--avatarRadius);
object-fit: contain;
}
span {
line-height: 24px;
margin: 0 0.1em 0 0.2em;
}
small {
margin-left: .5em;
color: $fallback--faint;
color: var(--faint, $fallback--faint);
}
}
}
</style>
diff --git a/src/components/registration/registration.vue b/src/components/registration/registration.vue
index 0966645ded..b90930825d 100644
--- a/src/components/registration/registration.vue
+++ b/src/components/registration/registration.vue
@@ -1,128 +1,124 @@
<template>
<div class="settings panel panel-default">
<div class="panel-heading">
{{$t('registration.registration')}}
</div>
<div class="panel-body">
<form v-on:submit.prevent='submit(user)' class='registration-form'>
<div class='container'>
<div class='text-fields'>
<div class='form-group'>
<label for='username'>{{$t('login.username')}}</label>
<input :disabled="registering" v-model='user.username' class='form-control' id='username' placeholder='e.g. lain'>
</div>
<div class='form-group'>
<label for='fullname'>{{$t('registration.fullname')}}</label>
<input :disabled="registering" v-model='user.fullname' class='form-control' id='fullname' placeholder='e.g. Lain Iwakura'>
</div>
<div class='form-group'>
<label for='email'>{{$t('registration.email')}}</label>
<input :disabled="registering" v-model='user.email' class='form-control' id='email' type="email">
</div>
<div class='form-group'>
<label for='bio'>{{$t('registration.bio')}}</label>
<input :disabled="registering" v-model='user.bio' class='form-control' id='bio'>
</div>
<div class='form-group'>
<label for='password'>{{$t('login.password')}}</label>
<input :disabled="registering" v-model='user.password' class='form-control' id='password' type='password'>
</div>
<div class='form-group'>
<label for='password_confirmation'>{{$t('registration.password_confirm')}}</label>
<input :disabled="registering" v-model='user.confirm' class='form-control' id='password_confirmation' type='password'>
</div>
<!--
<div class='form-group'>
<label for='captcha'>Captcha</label>
<img src='/qvittersimplesecurity/captcha.jpg' alt='captcha' class='captcha'>
<input :disabled="registering" v-model='user.captcha' placeholder='Enter captcha' type='test' class='form-control' id='captcha'>
</div>
-->
<div class='form-group'>
<button :disabled="registering" type='submit' class='btn btn-default'>{{$t('general.submit')}}</button>
</div>
</div>
<div class='terms-of-service' v-html="termsofservice">
</div>
</div>
<div v-if="error" class='form-group'>
<div class='error'>{{error}}</div>
</div>
</form>
</div>
</div>
</template>
<script src="./registration.js"></script>
<style lang="scss">
@import '../../_variables.scss';
.registration-form {
display: flex;
flex-direction: column;
margin: 0.6em;
.container {
display: flex;
flex-direction: row;
//margin-bottom: 1em;
}
.terms-of-service {
flex: 0 1 50%;
margin: 0.8em;
}
.text-fields {
margin-top: 0.6em;
flex: 1 0;
display: flex;
flex-direction: column;
}
.form-group {
display: flex;
flex-direction: column;
padding: 0.3em 0.0em 0.3em;
line-height:24px;
}
form textarea {
- border-radius: 5px;
line-height:16px;
resize: vertical;
}
- input {
- border-radius: 5px;
- }
-
.captcha {
max-width: 350px;
margin-bottom: 0.4em;
}
.btn {
//align-self: flex-start;
//width: 10em;
margin-top: 0.6em;
height: 28px;
}
.error {
- border-radius: 5px;
+ border-radius: $fallback--tooltipRadius;
+ border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
text-align: center;
margin: 0.5em 0.6em 0;
background-color: $fallback--cRed;
background-color: var(--cRed, $fallback--cRed);
min-height: 28px;
line-height: 28px;
}
}
@media all and (max-width: 959px) {
.registration-form .container {
flex-direction: column-reverse;
}
}
</style>
diff --git a/src/components/settings/settings.vue b/src/components/settings/settings.vue
index 98f70f7b57..9f1956873d 100644
--- a/src/components/settings/settings.vue
+++ b/src/components/settings/settings.vue
@@ -1,86 +1,93 @@
<template>
<div class="settings panel panel-default">
<div class="panel-heading">
{{$t('settings.settings')}}
</div>
<div class="panel-body">
<div class="setting-item">
<h2>{{$t('settings.theme')}}</h2>
<style-switcher></style-switcher>
</div>
<div class="setting-item">
<h2>{{$t('settings.filtering')}}</h2>
<p>{{$t('settings.filtering_explanation')}}</p>
<textarea id="muteWords" v-model="muteWordsString"></textarea>
</div>
<div class="setting-item">
<h2>{{$t('settings.attachments')}}</h2>
<ul class="setting-list">
<li>
<input type="checkbox" id="hideAttachments" v-model="hideAttachmentsLocal">
<label for="hideAttachments">{{$t('settings.hide_attachments_in_tl')}}</label>
</li>
<li>
<input type="checkbox" id="hideAttachmentsInConv" v-model="hideAttachmentsInConvLocal">
<label for="hideAttachmentsInConv">{{$t('settings.hide_attachments_in_convo')}}</label>
</li>
<li>
<input type="checkbox" id="hideNsfw" v-model="hideNsfwLocal">
<label for="hideNsfw">{{$t('settings.nsfw_clickthrough')}}</label>
</li>
<li>
<input type="checkbox" id="autoload" v-model="autoloadlocal">
<label for="autoload">{{$t('settings.autoload')}}</label>
</li>
<li>
<input type="checkbox" id="streaming" v-model="streamingLocal">
<label for="streaming">{{$t('settings.streaming')}}</label>
</li>
<li>
<input type="checkbox" id="hoverPreview" v-model="hoverPreviewLocal">
<label for="hoverPreview">{{$t('settings.reply_link_preview')}}</label>
</li>
<li>
<input type="checkbox" id="stopGifs" v-model="stopGifs">
<label for="stopGifs">{{$t('settings.stop_gifs')}}</label>
</li>
</ul>
</div>
</div>
</div>
</template>
<script src="./settings.js">
</script>
<style lang="scss">
- .setting-item {
- margin: 1em 1em 1.4em;
+@import '../../_variables.scss';
- textarea {
- width: 100%;
- height: 100px;
- }
+.setting-item {
+ margin: 1em 1em 1.4em;
- .old-avatar {
- width: 128px;
- border-radius: 5px;
- }
+ textarea {
+ width: 100%;
+ height: 100px;
+ }
- .new-avatar {
- object-fit: cover;
- width: 128px;
- height: 128px;
- border-radius: 5px;
- }
+ .old-avatar {
+ width: 128px;
+ border-radius: $fallback--avatarRadius;
+ border-radius: var(--avatarRadius, $fallback--avatarRadius);
+ }
- .btn {
- margin-top: 1em;
- min-height: 28px;
- width: 10em;
- }
- }
- .setting-list {
- list-style-type: none;
- }
+ .new-avatar {
+ object-fit: cover;
+ width: 128px;
+ height: 128px;
+ border-radius: $fallback--avatarRadius;
+ border-radius: var(--avatarRadius, $fallback--avatarRadius);
+ }
+
+ .btn {
+ margin-top: 1em;
+ min-height: 28px;
+ width: 10em;
+ }
+}
+.setting-list {
+ list-style-type: none;
+ li {
+ margin-bottom: 0.5em;
+ }
+}
</style>
diff --git a/src/components/status/status.vue b/src/components/status/status.vue
index 80f6a3157e..14ffd92287 100644
--- a/src/components/status/status.vue
+++ b/src/components/status/status.vue
@@ -1,473 +1,475 @@
<template>
<div class="status-el" v-if="compact">
<div @click.prevent="linkClicked" class="status-content" v-html="status.statusnet_html"></div>
<div v-if="loggedIn">
<div class='status-actions'>
<div>
<a href="#" v-on:click.prevent="toggleReplying">
<i class="icon-reply" :class="{'icon-reply-active': replying}"></i>
</a>
</div>
<retweet-button :loggedIn="loggedIn" :status=status></retweet-button>
<favorite-button :loggedIn="loggedIn" :status=status></favorite-button>
</div>
</div>
<post-status-form class="reply-body" :reply-to="status.id" :attentions="status.attentions" :repliedUser="status.user" v-on:posted="toggleReplying" v-if="replying"/>
</div>
<div class="status-el status-fadein" v-else-if="!status.deleted" v-bind:class="[{ 'status-el_focused': isFocused }, { 'status-conversation': inConversation }]" >
<template v-if="muted">
<div class="media status container muted">
<small><router-link :to="{ name: 'user-profile', params: { id: status.user.id } }">{{status.user.screen_name}}</router-link></small>
<small class="muteWords">{{muteWordHits.join(', ')}}</small>
<a href="#" class="unmute" @click.prevent="toggleMute"><i class="icon-eye-off"></i></a>
</div>
</template>
<template v-if="!muted">
<div v-if="retweet" class="media container retweet-info">
<div class="media-left">
<i class='fa icon-retweet retweeted'></i>
</div>
<div class="media-body">
Repeated by <a :href="statusoid.user.statusnet_profile_url" style="font-weight: bold;" :title="'@'+statusoid.user.screen_name">{{retweeter}}</a>
</div>
</div>
<div class="media status container">
<div class="media-left">
<a :href="status.user.statusnet_profile_url">
<StillImage @click.native.prevent="toggleUserExpanded" :class="{retweeted: retweet}" class='avatar' :src="status.user.profile_image_url_original"/>
<StillImage v-if="retweet" class='avatar avatar-retweeter' :src="statusoid.user.profile_image_url_original"/>
</a>
</div>
<div class="media-body">
<div class="usercard" v-if="userExpanded">
<user-card-content :user="status.user" :switcher="false"></user-card-content>
</div>
<div class="user-content">
<div class="media-heading">
<div class="name-and-links">
<h4 class="user-name">{{status.user.name}}</h4>
<div class="links">
<h4>
<small><router-link :to="{ name: 'user-profile', params: { id: status.user.id } }">{{status.user.screen_name}}</router-link></small>
<small v-if="status.in_reply_to_screen_name"> >
<router-link :to="{ name: 'user-profile', params: { id: status.in_reply_to_user_id } }">
{{status.in_reply_to_screen_name}}
</router-link>
</small>
<template v-if="isReply">
<small>
<a href="#" @click.prevent="gotoOriginal(status.in_reply_to_status_id)"><i class="icon-reply" @mouseenter="replyEnter(status.in_reply_to_status_id, $event)" @mouseout="replyLeave()"></i></a>
</small>
</template>
-
<small>
<router-link :to="{ name: 'conversation', params: { id: status.id } }">
<timeago :since="status.created_at" :auto-update="60"></timeago>
</router-link>
</small>
</h4>
</div>
<h4 class="replies" v-if="inConversation">
<small v-if="replies.length">Replies:</small>
<small v-for="reply in replies">
<a href="#" @click.prevent="gotoOriginal(reply.id)" @mouseenter="replyEnter(reply.id, $event)" @mouseout="replyLeave()">{{reply.name}} </a>
</small>
</h4>
</div>
<div class="heading-icons">
<a href="#" @click.prevent="toggleMute" v-if="unmuted"><i class="icon-eye-off"></i></a>
<a :href="status.external_url" target="_blank" v-if="!status.is_local" class="source_url"><i class="icon-binoculars"></i></a>
<template v-if="expandable">
<a href="#" @click.prevent="toggleExpanded" class="expand"><i class="icon-plus-squared"></i></a>
</template>
</div>
</div>
<div class="status-preview" v-if="showPreview && preview">
<StillImage class="avatar" :src="preview.user.profile_image_url_original"/>
<div class="text">
<h4>
{{ preview.user.name }}
<small><a>{{ preview.user.screen_name}}</a></small>
</h4>
<div @click.prevent="linkClicked" class="status-content" v-html="preview.statusnet_html"></div>
</div>
</div>
<div class="status-preview status-preview-loading" v-else-if="showPreview">
<i class="icon-spin4 animate-spin"></i>
</div>
<div @click.prevent="linkClicked" class="status-content" v-html="status.statusnet_html"></div>
<div v-if='status.attachments' class='attachments'>
<attachment v-if="!hideAttachments" :status-id="status.id" :nsfw="status.nsfw" :attachment="attachment" v-for="attachment in status.attachments" :key="attachment.id">
</attachment>
</div>
</div>
<div class='status-actions'>
<div v-if="loggedIn">
<a href="#" v-on:click.prevent="toggleReplying">
<i class="icon-reply" :class="{'icon-reply-active': replying}"></i>
</a>
</div>
<retweet-button :loggedIn="loggedIn" :status=status></retweet-button>
<favorite-button :loggedIn="loggedIn" :status=status></favorite-button>
<delete-button :status=status></delete-button>
</div>
</div>
</div>
<div class="status container" v-if="replying">
<div class="reply-left"/>
<post-status-form class="reply-body" :reply-to="status.id" :attentions="status.attentions" :repliedUser="status.user" v-on:posted="toggleReplying"/>
</div>
</template>
</div>
</template>
<script src="./status.js" ></script>
<style lang="scss">
@import '../../_variables.scss';
status-text-container {
display: block;
}
.status-preview {
position: absolute;
max-width: 34em;
padding: 0.5em;
display: flex;
background-color: $fallback--bg;
background-color: var(--bg, $fallback--bg);
border-color: $fallback--border;
border-color: var(--border, $fallback--border);
border-style: solid;
border-width: 1px;
- border-radius: 4px;
+ border-radius: $fallback--tooltipRadius;
+ border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5);
margin-top: 0.5em;
margin-left: 1em;
z-index: 50;
.avatar {
flex-shrink: 0;
width: 32px;
height: 32px;
- border-radius: 50%;
+ border-radius: $fallback--avatarAltRadius;
+ border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius);
}
.text {
h4 {
margin-bottom: 0.4em;
small {
font-weight: lighter;
}
}
padding: 0 0.5em 0.5em 0.5em;
}
}
.status-preview-loading {
display: block;
font-size: 2em;
min-width: 8em;
text-align: center;
}
.status-el {
hyphens: auto;
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-word;
border-left-width: 0px;
line-height: 18px;
background-color: $fallback--bg;
background-color: var(--bg, $fallback--bg);
border-color: $fallback--border;
border-color: var(--border, $fallback--border);
&_focused {
background-color: $fallback--lightBg;
background-color: var(--lightBg, $fallback--lightBg);
}
.usercard {
border-color: $fallback--border;
border-color: var(--border, $fallback--border);
}
.timeline & {
border-bottom-width: 1px;
border-bottom-style: solid;
}
.notify {
.avatar {
border-width: 3px;
border-style: solid;
}
}
.media-body {
flex: 1;
padding-left: 0.5em;
}
.user-content {
min-height: 52px;
padding-top: 1px;
}
.media-heading {
display: flex;
min-height: 1.4em;
margin-bottom: 0.3em;
small {
font-weight: lighter;
}
h4 {
margin-right: 0.4em;
}
.name-and-links {
flex: 1 0;
display: flex;
flex-wrap: wrap;
}
.replies {
flex-basis: 100%;
}
}
.source_url {
}
.expand {
margin-right: -0.3em;
}
a {
display: inline-block;
word-break: break-all;
}
.status-content {
margin: 3px 15px 4px 0;
max-height: 400px;
overflow-y: auto;
overflow-x: hidden;
img, video {
max-width: 100%;
max-height: 400px;
vertical-align: middle;
object-fit: contain;
}
blockquote {
margin: 0.2em 0 0.2em 2em;
font-style: italic;
}
}
p {
margin: 0;
margin-top: 0.2em;
margin-bottom: 0.5em;
}
.media-left {
margin: 0.2em 0.3em 0 0;
.avatar {
float: right;
- border-radius: 5px;
+ border-radius: $fallback--avatarRadius;
+ border-radius: var(--avatarRadius, $fallback--avatarRadius);
}
}
.retweet-info {
padding: 0.7em 0 0 0.6em;
.media-left {
display: flex;
i {
align-self: center;
text-align: right;
flex: 1;
padding-right: 0.3em;
}
}
}
}
.status-fadein {
animation-duration: 0.5s;
animation-name: fadein;
}
@keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.greentext {
color: green;
}
.status-conversation {
border-left-style: solid;
}
.status-actions {
padding-top: 0.15em;
width: 100%;
display: flex;
div, favorite-button {
max-width: 6em;
flex: 1;
}
}
.icon-reply:hover {
color: $fallback--cBlue;
color: var(--cBlue, $fallback--cBlue);
}
.icon-reply.icon-reply-active {
color: $fallback--cBlue;
color: var(--cBlue, $fallback--cBlue);
}
.status .avatar {
width: 48px;
height: 48px;
border-radius: 5px;
overflow: hidden;
img {
width: 100%;
height: 100%;
}
&.animated::before {
display: none;
}
&.retweeted {
width: 40px;
height: 40px;
margin-right: 8px;
margin-bottom: 8px;
}
}
.status:hover .animated.avatar {
canvas {
display: none;
}
img {
visibility: visible;
}
}
.status img.avatar-retweeter {
width: 24px;
height: 24px;
position: absolute;
margin-left: 24px;
margin-top: 24px;
}
.status.compact .avatar {
width: 32px;
}
.status {
padding: 0.4em 0.7em 0.45em 0.7em;
border-left: 4px $fallback--cRed;
border-left: 4px var(--cRed, $fallback--cRed);
border-left-style: inherit;
}
.status-conversation:last-child {
border-bottom: none;
}
.timeline .panel.timeline {
- border-radius: 10px;
overflow: hidden;
}
.muted {
padding: 0.1em 0.4em 0.1em 0.8em;
button {
margin-left: auto;
}
.muteWords {
margin-left: 10px;
}
}
a.unmute {
display: block;
margin-left: auto;
}
.reply-left {
flex: 0;
min-width: 48px;
}
.reply-body {
flex: 1;
}
@media all and (max-width: 960px) {
.status-el {
.name-and-links {
margin-left: -0.25em;
}
}
.status {
max-width: 100%;
}
.status .avatar {
width: 40px;
height: 40px;
&.retweeted {
width: 34px;
height: 34px;
margin-right: 8px;
margin-bottom: 8px;
}
}
.status .avatar-retweeter {
width: 22px;
height: 22px;
position: absolute;
margin-left: 18px;
margin-top: 18px;
}
}
</style>
diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js
index 6e41433870..5e5ba266e2 100644
--- a/src/components/style_switcher/style_switcher.js
+++ b/src/components/style_switcher/style_switcher.js
@@ -1,66 +1,89 @@
import { rgbstr2hex } from '../../services/color_convert/color_convert.js'
export default {
data () {
return {
availableStyles: [],
selected: this.$store.state.config.theme,
bgColorLocal: '',
fgColorLocal: '',
textColorLocal: '',
- linkColorLocal: ''
+ linkColorLocal: '',
+ redColorLocal: '#ff0000',
+ blueColorLocal: '#0095ff',
+ greenColorLocal: '#0fa00f',
+ orangeColorLocal: '#E3FF00'
}
},
created () {
const self = this
window.fetch('/static/styles.json')
.then((data) => data.json())
.then((themes) => {
self.availableStyles = themes
})
},
mounted () {
this.bgColorLocal = rgbstr2hex(this.$store.state.config.colors.bg)
this.fgColorLocal = rgbstr2hex(this.$store.state.config.colors.lightBg)
this.textColorLocal = rgbstr2hex(this.$store.state.config.colors.fg)
this.linkColorLocal = rgbstr2hex(this.$store.state.config.colors.link)
+
+ this.redColorLocal = rgbstr2hex(this.$store.state.config.colors.cRed || this.redColorLocal)
+ this.blueColorLocal = rgbstr2hex(this.$store.state.config.colors.cBlue || this.blueColorLocal)
+ this.greenColorLocal = rgbstr2hex(this.$store.state.config.colors.cGreen || this.greenColorLocal)
+ this.orangeColorLocal = rgbstr2hex(this.$store.state.config.colors.cOrange || this.orangeColorLocal)
},
methods: {
setCustomTheme () {
if (!this.bgColorLocal && !this.fgColorLocal && !this.linkColorLocal) {
// reset to picked themes
}
const rgb = (hex) => {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null
}
const bgRgb = rgb(this.bgColorLocal)
const fgRgb = rgb(this.fgColorLocal)
const textRgb = rgb(this.textColorLocal)
const linkRgb = rgb(this.linkColorLocal)
+
+ const redRgb = rgb(this.redColorLocal)
+ const blueRgb = rgb(this.blueColorLocal)
+ const greenRgb = rgb(this.greenColorLocal)
+ const orangeRgb = rgb(this.orangeColorLocal)
+
if (bgRgb && fgRgb && linkRgb) {
this.$store.dispatch('setOption', {
name: 'customTheme',
value: {
fg: fgRgb,
bg: bgRgb,
text: textRgb,
- link: linkRgb
+ link: linkRgb,
+ cRed: redRgb,
+ cBlue: blueRgb,
+ cGreen: greenRgb,
+ cOrange: orangeRgb
}})
}
}
},
watch: {
selected () {
this.bgColorLocal = this.selected[1]
this.fgColorLocal = this.selected[2]
this.textColorLocal = this.selected[3]
this.linkColorLocal = this.selected[4]
+ this.redColorLocal = this.selected[5]
+ this.blueColorLocal = this.selected[6]
+ this.greenColorLocal = this.selected[7]
+ this.orangeColorLocal = this.selected[8]
}
}
}
diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue
index e6a00275ff..e2853d01c6 100644
--- a/src/components/style_switcher/style_switcher.vue
+++ b/src/components/style_switcher/style_switcher.vue
@@ -1,110 +1,131 @@
<template>
<div>
<p>{{$t('settings.presets')}}</p>
<select v-model="selected" class="style-switcher">
<option v-for="style in availableStyles" :value="style">{{style[0]}}</option>
</select>
<p>{{$t('settings.theme_help')}}</p>
<div class="color-container">
<div class="color-item">
<label for="bgcolor" class="theme-color-lb">{{$t('settings.background')}}</label>
<input id="bgcolor" class="theme-color-cl" type="color" v-model="bgColorLocal">
<input id="bgcolor-t" class="theme-color-in" type="text" v-model="bgColorLocal">
</div>
<div class="color-item">
<label for="fgcolor" class="theme-color-lb">{{$t('settings.foreground')}}</label>
<input id="fgcolor" class="theme-color-cl" type="color" v-model="fgColorLocal">
<input id="fgcolor-t" class="theme-color-in" type="text" v-model="fgColorLocal">
</div>
<div class="color-item">
<label for="textcolor" class="theme-color-lb">{{$t('settings.text')}}</label>
<input id="textcolor" class="theme-color-cl" type="color" v-model="textColorLocal">
<input id="textcolor-t" class="theme-color-in" type="text" v-model="textColorLocal">
</div>
<div class="color-item">
<label for="linkcolor" class="theme-color-lb">{{$t('settings.links')}}</label>
<input id="linkcolor" class="theme-color-cl" type="color" v-model="linkColorLocal">
<input id="linkcolor-t" class="theme-color-in" type="text" v-model="linkColorLocal">
</div>
</div>
+ <div class="color-container additional colors">
+ <div class="color-item">
+ <label for="redcolor" class="theme-color-lb">{{$t('settings.cRed')}}</label>
+ <input id="redcolor" class="theme-color-cl" type="color" v-model="redColorLocal">
+ <input id="redcolor-t" class="theme-color-in" type="text" v-model="redColorLocal">
+ </div>
+ <div class="color-item">
+ <label for="bluecolor" class="theme-color-lb">{{$t('settings.cBlue')}}</label>
+ <input id="bluecolor" class="theme-color-cl" type="color" v-model="blueColorLocal">
+ <input id="bluecolor-t" class="theme-color-in" type="text" v-model="blueColorLocal">
+ </div>
+ <div class="color-item">
+ <label for="greencolor" class="theme-color-lb">{{$t('settings.cGreen')}}</label>
+ <input id="greencolor" class="theme-color-cl" type="color" v-model="greenColorLocal">
+ <input id="greencolor-t" class="theme-color-in" type="green" v-model="greenColorLocal">
+ </div>
+ <div class="color-item">
+ <label for="orangecolor" class="theme-color-lb">{{$t('settings.cOrange')}}</label>
+ <input id="orangecolor" class="theme-color-cl" type="color" v-model="orangeColorLocal">
+ <input id="orangecolor-t" class="theme-color-in" type="text" v-model="orangeColorLocal">
+ </div>
+ </div>
<div>
<div class="panel">
<div class="panel-heading" :style="{ 'background-color': fgColorLocal, 'color': textColorLocal }">Preview</div>
<div class="panel-body theme-preview-content" :style="{ 'background-color': bgColorLocal, 'color': textColorLocal }">
<h4>Content</h4>
<br>
A bunch of more content and
<a :style="{ 'color': linkColorLocal }">a nice lil' link</a>
<br>
<button class="btn" :style="{ 'background-color': fgColorLocal, 'color': textColorLocal }">Button</button>
</div>
</div>
</div>
<button class="btn" @click="setCustomTheme">{{$t('general.apply')}}</button>
</div>
</template>
<script src="./style_switcher.js"></script>
<style lang="scss">
@import '../../_variables.scss';
.style-switcher {
margin-right: 1em;
}
.color-container {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.color-item {
min-width: 20em;
display:flex;
flex: 1 1 0;
align-items: baseline;
margin: 5px 6px 5px 0;
label {
color: var(--faint, $fallback--faint);
}
}
.theme-color-cl {
border: 0;
box-shadow: none;
background: transparent;
}
.theme-color-cl,
.theme-color-in {
margin-left: 4px;
- border-radius: 2px;
}
.theme-color-in {
padding: 5px;
min-width: 4em;
max-width: 7em;
flex: 1;
}
.theme-color-lb {
flex: 2;
min-width: 7em;
max-width: 10em;
}
.theme-color-cl {
padding: 1px;
max-width: 8em;
align-self: stretch;
height: 100%;
flex: 0;
min-width: 2em;
cursor: pointer;
}
.theme-preview-content {
padding: 20px;
}
</style>
diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue
index c3cc939a18..aa3a8bff86 100644
--- a/src/components/timeline/timeline.vue
+++ b/src/components/timeline/timeline.vue
@@ -1,115 +1,112 @@
<template>
<div class="timeline panel panel-default" v-if="viewing == 'statuses'">
<div class="panel-heading timeline-heading">
<div class="title">
{{title}}
</div>
<button @click.prevent="showNewStatuses" class="loadmore-button" v-if="timeline.newStatusCount > 0 && !timelineError">
{{$t('timeline.show_new')}}{{newStatusCountStr}}
</button>
<div @click.prevent class="loadmore-text" v-if="timelineError">
{{$t('timeline.error_fetching')}}
</div>
<div @click.prevent class="loadmore-text" v-if="!timeline.newStatusCount > 0 && !timelineError">
{{$t('timeline.up_to_date')}}
</div>
</div>
<div class="panel-body">
<div class="timeline">
<status-or-conversation v-for="status in timeline.visibleStatuses" :key="status.id" v-bind:statusoid="status"></status-or-conversation>
<a href="#" v-on:click.prevent='fetchOlderStatuses()' v-if="!timeline.loading">
<div class="new-status-notification text-center">{{$t('timeline.load_older')}}</div>
</a>
<div class="new-status-notification text-center" v-else>...</div>
</div>
</div>
</div>
<div class="timeline panel panel-default" v-else-if="viewing == 'followers'">
<div class="panel-heading timeline-heading">
<div class="title">
{{$t('user_card.followers')}}
</div>
</div>
<div class="panel-body">
<div class="timeline">
<user-card v-for="follower in followers" :key="follower.id" :user="follower" :showFollows="false"></user-card>
</div>
</div>
</div>
<div class="timeline panel panel-default" v-else-if="viewing == 'friends'">
<div class="panel-heading timeline-heading">
<div class="title">
{{$t('user_card.followees')}}
</div>
</div>
<div class="panel-body">
<div class="timeline">
<user-card v-for="friend in friends" :key="friend.id" :user="friend" :showFollows="true"></user-card>
</div>
</div>
</div>
</template>
<script src="./timeline.js"></script>
<style lang="scss">
@import '../../_variables.scss';
.timeline {
.timeline-heading {
position: relative;
display: flex;
}
.title {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 70%;
}
.loadmore-button {
position: absolute;
right: 0.6em;
font-size: 14px;
min-width: 6em;
height: 1.8em;
line-height: 100%;
}
.loadmore-text {
position: absolute;
right: 0.6em;
font-size: 14px;
min-width: 6em;
- border-radius: 5px;
background-color: transparent;
color: $fallback--faint;
color: var(--faint, $fallback--faint);
font-family: sans-serif;
text-align: center;
padding: 0 0.5em 0 0.5em;
opacity: 0.8;
}
.error {
color: $fallback--lightFg;
color: var(--lightFg, $fallback--lightFg);
background-color: $fallback--cRed;
background-color: var(--cRed, $fallback--cRed);
}
}
.new-status-notification {
position:relative;
margin-top: -1px;
font-size: 1.1em;
border-width: 1px 0 0 0;
border-style: solid;
- border-radius: 0 0 10px 10px;
- border-color: $fallback--border;
border-color: var(--border, $fallback--border);
padding: 10px;
z-index: 1;
}
</style>
diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue
index 6487d2a6cc..660689d29f 100644
--- a/src/components/user_card/user_card.vue
+++ b/src/components/user_card/user_card.vue
@@ -1,76 +1,79 @@
<template>
<div class="card">
<a href="#">
<img @click.prevent="toggleUserExpanded" class="avatar" :src="user.profile_image_url">
</a>
<div class="usercard" v-if="userExpanded">
<user-card-content :user="user" :switcher="false"></user-card-content>
</div>
<div class="name-and-screen-name" v-else>
<div class="user-name">
{{ user.name }}
<span class="follows-you" v-if="!userExpanded && showFollows && user.follows_you">
{{ $t('user_card.follows_you') }}
</span>
</div>
<a :href="user.statusnet_profile_url" target="blank"><div class="user-screen-name">@{{ user.screen_name }}</div></a>
</div>
</div>
</template>
<script src="./user_card.js"></script>
<style lang="scss">
@import '../../_variables.scss';
.name-and-screen-name {
margin-left: 0.7em;
margin-top:0.0em;
margin-right: 2em;
text-align: left;
width: 100%;
}
.follows-you {
margin-left: 2em;
float: right;
}
.card {
display: flex;
flex: 1 0;
padding-top: 0.6em;
padding-right: 1em;
padding-bottom: 0.6em;
padding-left: 1em;
border-bottom: 1px solid;
margin: 0;
border-bottom-color: $fallback--border;
border-bottom-color: var(--border, $fallback--border);
.avatar {
margin-top: 0.2em;
width:32px;
height: 32px;
- border-radius: 50%;
+ border-radius: $fallback--avatarAltRadius;
+ border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius);
}
}
.usercard {
width: fill-available;
margin: 0.2em 0 0.7em 0;
- border-radius: 10px;
+ border-radius: $fallback--panelRadius;
+ border-radius: var(--panelRadius, $fallback--panelRadius);
border-style: solid;
- border-color: inherit;
+ border-color: $fallback--border;
+ border-color: var(--border, $fallback--border);
border-width: 1px;
overflow: hidden;
.panel-heading {
background: transparent;
}
p {
margin-bottom: 0;
}
}
</style>
diff --git a/src/components/user_card_content/user_card_content.vue b/src/components/user_card_content/user_card_content.vue
index ca3da632c1..3f4689fd05 100644
--- a/src/components/user_card_content/user_card_content.vue
+++ b/src/components/user_card_content/user_card_content.vue
@@ -1,260 +1,310 @@
<template>
<div id="heading" class="profile-panel-background" :style="headingStyle">
<div class="panel-heading text-center">
<div class='user-info'>
<router-link to='/user-settings' style="float: right; margin-top:16px;" v-if="!isOtherUser">
<i class="icon-cog usersettings"></i>
</router-link>
<div class='container'>
<router-link :to="{ name: 'user-profile', params: { id: user.id } }">
<StillImage class="avatar" :src="user.profile_image_url_original"/>
</router-link>
<span class="glyphicon glyphicon-user"></span>
<div class="name-and-screen-name">
<div class='user-name'>{{user.name}}</div>
<router-link :to="{ name: 'user-profile', params: { id: user.id } }">
<div class='user-screen-name'>@{{user.screen_name}}</div>
</router-link>
</div>
</div>
<div v-if="isOtherUser" class="user-interactions">
<div v-if="user.follows_you && loggedIn" class="following">
{{ $t('user_card.follows_you') }}
</div>
<div class="follow" v-if="loggedIn">
<span v-if="user.following">
<!--Following them!-->
<button @click="unfollowUser" class="pressed">
{{ $t('user_card.following') }}
</button>
</span>
<span v-if="!user.following">
<button @click="followUser">
{{ $t('user_card.follow') }}
</button>
</span>
</div>
<div class='mute' v-if='isOtherUser'>
<span v-if='user.muted'>
<button @click="toggleMute" class="pressed">
{{ $t('user_card.muted') }}
</button>
</span>
<span v-if='!user.muted'>
<button @click="toggleMute">
{{ $t('user_card.mute') }}
</button>
</span>
</div>
<div class="remote-follow" v-if='!loggedIn && user.is_local'>
<form method="POST" :action='subscribeUrl'>
<input type="hidden" name="nickname" :value="user.screen_name">
<input type="hidden" name="profile" value="">
<button click="submit" class="remote-button">
{{ $t('user_card.remote_follow') }}
</button>
</form>
</div>
<div class='block' v-if='isOtherUser && loggedIn'>
<span v-if='user.statusnet_blocking'>
<button @click="unblockUser" class="pressed">
{{ $t('user_card.blocked') }}
</button>
</span>
<span v-if='!user.statusnet_blocking'>
<button @click="blockUser">
{{ $t('user_card.block') }}
</button>
</span>
</div>
</div>
</div>
</div>
<div class="panel-body profile-panel-body">
<div class="user-counts">
<div class="user-count">
<a href="#" v-on:click.prevent="setProfileView('statuses')" v-if="switcher"><h5>{{ $t('user_card.statuses') }}</h5></a>
<h5 v-else>{{ $t('user_card.statuses') }}</h5>
<span>{{user.statuses_count}} <br><span class="dailyAvg">{{dailyAvg}} {{ $t('user_card.per_day') }}</span></span>
</div>
<div class="user-count">
<a href="#" v-on:click.prevent="setProfileView('friends')" v-if="switcher"><h5>{{ $t('user_card.followees') }}</h5></a>
<h5 v-else>{{ $t('user_card.followees') }}</h5>
<span>{{user.friends_count}}</span>
</div>
<div class="user-count">
<a href="#" v-on:click.prevent="setProfileView('followers')" v-if="switcher"><h5>{{ $t('user_card.followers') }}</h5></a>
<h5 v-else>{{ $t('user_card.followers') }}</h5>
<span>{{user.followers_count}}</span>
</div>
</div>
<p>{{user.description}}</p>
</div>
</div>
</template>
<script src="./user_card_content.js"></script>
<style lang="scss">
@import '../../_variables.scss';
.profile-panel-background {
background-size: cover;
- border-radius: 10px;
+ border-radius: $fallback--panelRadius;
+ border-radius: var(--panelRadius, $fallback--panelRadius);
.panel-heading {
padding: 0.6em 0em;
text-align: center;
}
}
.profile-panel-body {
top: -0em;
padding-top: 4em;
word-wrap: break-word;
background: linear-gradient(to bottom, rgba(0, 0, 0, 0), $fallback--bg 80%);
background: linear-gradient(to bottom, rgba(0, 0, 0, 0), var(--bg, $fallback--bg) 80%)
}
.user-info {
- color: white;
- padding: 0 16px 16px 16px;
- margin-bottom: -4em;
-
- .usersettings {
color: white;
- opacity: 0.8;
- }
+ padding: 0 16px 16px 16px;
+ margin-bottom: -4em;
.container{
padding: 16px 10px 4px 10px;
display: flex;
flex-wrap: wrap;
flex-direction: column;
align-content: flex-start;
justify-content: center;
max-height: 56px;
overflow: hidden;
}
.avatar {
border-radius: 5px;
flex: 1 0 100%;
width: 56px;
height: 56px;
box-shadow: 0px 1px 8px rgba(0,0,0,0.75);
object-fit: cover;
&.animated::before {
display: none;
}
}
&:hover .animated.avatar {
canvas {
display: none;
}
img {
visibility: visible;
}
}
text-shadow: 0px 1px 1.5px rgba(0, 0, 0, 1.0);
.name-and-screen-name {
display: block;
margin-left: 0.6em;
text-align: left;
text-overflow: ellipsis;
white-space: nowrap;
}
.user-name{
color: white;
}
.user-screen-name {
color: white;
font-weight: lighter;
font-size: 15px;
padding-right: 0.1em;
flex: 0 0 auto;
}
.user-interactions {
display: flex;
flex-flow: row wrap;
justify-content: space-between;
- div {
- flex: 1;
+ .usersettings {
+ color: white;
+ opacity: 0.8;
}
- 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;
+ .container{
+ padding: 16px 10px 4px 10px;
+ display: flex;
+ flex-wrap: wrap;
+ flex-direction: column;
+ align-content: flex-start;
+ justify-content: center;
+ max-height: 56px;
+ overflow: hidden;
}
- .mute {
- max-width: 220px;
- min-height: 28px;
+ img {
+ border-radius: $fallback--avatarRadius;
+ border-radius: var(--avatarRadius, $fallback--avatarRadius);
+ flex: 1 0 100%;
+ width: 56px;
+ height: 56px;
+ box-shadow: 0px 1px 8px rgba(0,0,0,0.75);
+ object-fit: cover;
}
- .remote-follow {
- max-width: 220px;
- min-height: 28px;
- }
+ text-shadow: 0px 1px 1.5px rgba(0, 0, 0, 1.0);
- .follow {
- max-width: 220px;
- min-height: 28px;
+ .name-and-screen-name {
+ display: block;
+ margin-left: 0.6em;
+ text-align: left;
+ text-overflow: ellipsis;
+ white-space: nowrap;
}
- button {
- width: 92%;
- height: 100%;
+ .user-name{
+ color: white;
}
- .remote-button {
- height: 28px !important;
- width: 92%;
+ .user-screen-name {
+ color: white;
+ font-weight: lighter;
+ font-size: 15px;
+ padding-right: 0.1em;
+ flex: 0 0 auto;
}
- .pressed {
- border-bottom-color: rgba(255, 255, 255, 0.2);
- border-top-color: rgba(0, 0, 0, 0.2);
+ .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;
+ }
+
+ .remote-follow {
+ max-width: 220px;
+ min-height: 28px;
+ }
+
+ .follow {
+ max-width: 220px;
+ min-height: 28px;
+ }
+
+ button {
+ width: 92%;
+ height: 100%;
+ }
+
+ .remote-button {
+ height: 28px !important;
+ width: 92%;
+ }
+
+ .pressed {
+ border-bottom-color: rgba(255, 255, 255, 0.2);
+ border-top-color: rgba(0, 0, 0, 0.2);
+ }
}
- }
}
.user-counts {
display: flex;
line-height:16px;
padding: 1em 1.5em 0em 1em;
text-align: center;
}
.user-count {
flex: 1;
h5 {
- font-size:1em;
+ color: white;
+ font-size:1em;
font-weight: bolder;
margin: 0 0 0.25em;
}
a {
text-decoration: none;
}
}
.dailyAvg {
- font-size: 0.8em;
- opacity: 0.5;
+ font-size: 0.8em;
+ opacity: 0.5;
}
</style>
diff --git a/src/components/user_finder/user_finder.vue b/src/components/user_finder/user_finder.vue
index e4453fe73d..f9362a7d54 100644
--- a/src/components/user_finder/user_finder.vue
+++ b/src/components/user_finder/user_finder.vue
@@ -1,40 +1,40 @@
<template>
<span class="user-finder-container">
<span class="finder-error" v-if="error">
<i class="icon-cancel user-finder-icon" @click="dismissError"/>
{{$t('finder.error_fetching_user')}}
</span>
<i class="icon-spin4 user-finder-icon animate-spin-slow" v-if="loading" />
<a href="#" v-if="hidden"><i class="icon-user-plus user-finder-icon" @click.prevent.stop="toggleHidden"/></a>
<span v-else>
<input class="user-finder-input" @keyup.enter="findUser(username)" v-model="username" :placeholder="$t('finder.find_user')" id="user-finder-input" type="text"/>
<i class="icon-cancel user-finder-icon" @click.prevent.stop="toggleHidden"/>
</span>
</span>
</template>
<script src="./user_finder.js"></script>
<style lang="scss">
@import '../../_variables.scss';
.user-finder-container {
height: 21px;
max-width: 100%;
}
.user-finder-input {
- border-radius: 5px;
max-width: 80%;
padding: 0.1em 0.2em 0.2em 0.2em;
}
.finder-error {
background-color: rgba(255, 48, 16, 0.65);
margin: 0.35em;
- border-radius: 5px;
+ border-radius: $fallback--tooltipRadius;
+ border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
padding: 0.25em;
color: $fallback--faint;
color: var(--faint, $fallback--faint);
}
</style>
diff --git a/src/components/user_panel/user_panel.vue b/src/components/user_panel/user_panel.vue
index 7da767b788..05af7bc366 100644
--- a/src/components/user_panel/user_panel.vue
+++ b/src/components/user_panel/user_panel.vue
@@ -1,21 +1,21 @@
<template>
<div class="user-panel">
<div v-if='user' class="panel panel-default" style="overflow: visible;">
<user-card-content :user="user" :switcher="false"></user-card-content>
<div class="panel-footer">
<post-status-form v-if='user'></post-status-form>
</div>
</div>
<login-form v-if='!user'></login-form>
</div>
</template>
<script src="./user_panel.js"></script>
<style lang="scss">
- .user-panel {
- .panel-heading {
- background: transparent;
+.user-panel {
+ .profile-panel-background .panel-heading {
+ background: transparent;
}
- }
+}
</style>
diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue
index 14d8cfe911..e6060451cb 100644
--- a/src/components/user_profile/user_profile.vue
+++ b/src/components/user_profile/user_profile.vue
@@ -1,24 +1,23 @@
<template>
<div>
<div v-if="user" class="user-profile panel panel-default">
<user-card-content :user="user" :switcher="true"></user-card-content>
</div>
<Timeline :title="$t('user_profile.timeline_title')" :timeline="timeline" :timeline-name="'user'" :user-id="userId"/>
</div>
</template>
<script src="./user_profile.js"></script>
<style lang="scss">
.user-profile {
- flex: 2;
- flex-basis: 500px;
- padding-bottom: 10px;
- border-radius: 10px;
- .panel-heading {
- background: transparent;
- }
+ flex: 2;
+ flex-basis: 500px;
+ padding-bottom: 10px;
+ .panel-heading {
+ background: transparent;
+ }
}
</style>
diff --git a/src/components/user_settings/user_settings.vue b/src/components/user_settings/user_settings.vue
index fecc67b835..39bb498cf2 100644
--- a/src/components/user_settings/user_settings.vue
+++ b/src/components/user_settings/user_settings.vue
@@ -1,105 +1,95 @@
<template>
<div class="settings panel panel-default">
<div class="panel-heading">
{{$t('settings.user_settings')}}
</div>
<div class="panel-body profile-edit">
<div class="setting-item">
<h3>{{$t('settings.name_bio')}}</h3>
<p>{{$t('settings.name')}}</p>
<input class='name-changer' id='username' v-model="newname"></input>
<p>{{$t('settings.bio')}}</p>
<textarea class="bio" v-model="newbio"></textarea>
<button :disabled='newname.length <= 0' class="btn btn-default" @click="updateProfile">{{$t('general.submit')}}</button>
</div>
<div class="setting-item">
<h3>{{$t('settings.avatar')}}</h3>
<p>{{$t('settings.current_avatar')}}</p>
<img :src="user.profile_image_url_original" class="old-avatar"></img>
<p>{{$t('settings.set_new_avatar')}}</p>
<img class="new-avatar" v-bind:src="previews[0]" v-if="previews[0]">
</img>
<div>
<input type="file" @change="uploadFile(0, $event)" ></input>
</div>
<i class="icon-spin4 animate-spin" v-if="uploading[0]"></i>
<button class="btn btn-default" v-else-if="previews[0]" @click="submitAvatar">{{$t('general.submit')}}</button>
</div>
<div class="setting-item">
<h3>{{$t('settings.profile_banner')}}</h3>
<p>{{$t('settings.current_profile_banner')}}</p>
<img :src="user.cover_photo" class="banner"></img>
<p>{{$t('settings.set_new_profile_banner')}}</p>
<img class="banner" v-bind:src="previews[1]" v-if="previews[1]">
</img>
<div>
<input type="file" @change="uploadFile(1, $event)" ></input>
</div>
<i class=" icon-spin4 animate-spin uploading" v-if="uploading[1]"></i>
<button class="btn btn-default" v-else-if="previews[1]" @click="submitBanner">{{$t('general.submit')}}</button>
</div>
<div class="setting-item">
<h3>{{$t('settings.profile_background')}}</h3>
<p>{{$t('settings.set_new_profile_background')}}</p>
<img class="bg" v-bind:src="previews[2]" v-if="previews[2]">
</img>
<div>
<input type="file" @change="uploadFile(2, $event)" ></input>
</div>
<i class=" icon-spin4 animate-spin uploading" v-if="uploading[2]"></i>
<button class="btn btn-default" v-else-if="previews[2]" @click="submitBg">{{$t('general.submit')}}</button>
</div>
<div class="setting-item" v-if="pleromaBackend">
<h3>{{$t('settings.follow_import')}}</h3>
<p>{{$t('settings.import_followers_from_a_csv_file')}}</p>
<form v-model="followImportForm">
<input type="file" ref="followlist" v-on:change="followListChange"></input>
</form>
<i class=" icon-spin4 animate-spin uploading" v-if="uploading[3]"></i>
<button class="btn btn-default" v-else @click="importFollows">{{$t('general.submit')}}</button>
<div v-if="followsImported">
<i class="icon-cross" @click="dismissImported"></i>
<p>{{$t('settings.follows_imported')}}</p>
</div>
<div v-else-if="followImportError">
<i class="icon-cross" @click="dismissImported"</i>
<p>{{$t('settings.follow_import_error')}}</p>
</div>
</div>
</div>
</div>
</template>
<script src="./user_settings.js">
</script>
<style lang="scss">
.profile-edit {
- .name-changer {
- border-radius: 5px;
- padding: 0.2em 0.2em 0.2em 0.2em;
- }
- .name-submit {
- padding: 0.2em 0.5em 0.2em 0.5em;
- }
- .bio {
- border-radius: 5px;
- margin: 0;
- }
+ .bio {
+ margin: 0;
+ }
- input[type=file] {
- padding: 5px;
- border-radius: 5px;
- }
+ input[type=file] {
+ padding: 5px;
+ }
- .banner {
- max-width: 400px;
- border-radius: 5px;
- }
+ .banner {
+ max-width: 400px;
+ }
- .uploading {
- font-size: 1.5em;
- margin: 0.25em;
- }
+ .uploading {
+ font-size: 1.5em;
+ margin: 0.25em;
+ }
}
</style>
diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js
index bedadb126c..503a0d72cf 100644
--- a/src/services/style_setter/style_setter.js
+++ b/src/services/style_setter/style_setter.js
@@ -1,130 +1,144 @@
import { times } from 'lodash'
import { rgb2hex, hex2rgb } from '../color_convert/color_convert.js'
// While this is not used anymore right now, I left it in if we want to do custom
// styles that aren't just colors, so user can pick from a few different distinct
// styles as well as set their own colors in the future.
const setStyle = (href, commit) => {
/***
What's going on here?
I want to make it easy for admins to style this application. To have
a good set of default themes, I chose the system from base16
(https://chriskempson.github.io/base16/) to style all elements. They
all have the base00..0F classes. So the only thing an admin needs to
do to style Pleroma is to change these colors in that one css file.
Some default things (body text color, link color) need to be set dy-
namically, so this is done here by waiting for the stylesheet to be
loaded and then creating an element with the respective classes.
It is a bit weird, but should make life for admins somewhat easier.
***/
const head = document.head
const body = document.body
body.style.display = 'none'
const cssEl = document.createElement('link')
cssEl.setAttribute('rel', 'stylesheet')
cssEl.setAttribute('href', href)
head.appendChild(cssEl)
const setDynamic = () => {
const baseEl = document.createElement('div')
body.appendChild(baseEl)
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.style.display = 'initial'
}
cssEl.addEventListener('load', setDynamic)
}
const setColors = (col, commit) => {
const head = document.head
const body = document.body
body.style.display = 'none'
const styleEl = document.createElement('style')
head.appendChild(styleEl)
const styleSheet = styleEl.sheet
const isDark = (col.text.r + col.text.g + col.text.b) > (col.bg.r + col.bg.g + col.bg.b)
let colors = {}
let mod = 10
if (isDark) {
mod = mod * -1
}
+ console.log(JSON.stringify(col, null, ' '))
colors.bg = rgb2hex(col.bg.r, col.bg.g, col.bg.b) // background
colors.lightBg = rgb2hex((col.bg.r + col.fg.r) / 2, (col.bg.g + col.fg.g) / 2, (col.bg.b + col.fg.b) / 2) // hilighted bg
colors.btn = rgb2hex(col.fg.r, col.fg.g, col.fg.b) // panels & buttons
colors.border = rgb2hex(col.fg.r - mod, col.fg.g - mod, col.fg.b - mod) // borders
colors.faint = rgb2hex(col.text.r + mod * 2, col.text.g + mod * 2, col.text.b + mod * 2) // faint text
colors.fg = rgb2hex(col.text.r, col.text.g, col.text.b) // text
colors.lightFg = rgb2hex(col.text.r - mod, col.text.g - mod, col.text.b - mod) // strong text
+
colors['base07'] = rgb2hex(col.text.r - mod * 2, col.text.g - mod * 2, col.text.b - mod * 2)
+
colors.link = rgb2hex(col.link.r, col.link.g, col.link.b) // links
colors.icon = rgb2hex((col.bg.r + col.text.r) / 2, (col.bg.g + col.text.g) / 2, (col.bg.b + col.text.b) / 2) // icons
- colors.cBlue = '#0095ff'
- colors.cRed = 'red'
- colors.cGreen = '#0fa00f'
- colors.cYellow = 'yellow'
- colors.cOrange = 'orange'
+
+ colors.cBlue = col.cBlue && rgb2hex(col.cBlue.r, col.cBlue.g, col.cBlue.b)
+ colors.cRed = col.cRed && rgb2hex(col.cRed.r, col.cRed.g, col.cRed.b)
+ colors.cGreen = col.cGreen && rgb2hex(col.cGreen.r, col.cGreen.g, col.cGreen.b)
+ colors.cOrange = col.cOrange && rgb2hex(col.cOrange.r, col.cOrange.g, col.cOrange.b)
styleSheet.toString()
styleSheet.insertRule(`body { ${Object.entries(colors).map(([k, v]) => `--${k}: ${v}`).join(';')} }`, 'index-max')
body.style.display = 'initial'
commit('setOption', { name: 'colors', value: colors })
commit('setOption', { name: 'customTheme', value: col })
}
const setPreset = (val, commit) => {
window.fetch('/static/styles.json')
.then((data) => data.json())
.then((themes) => {
const theme = themes[val] ? themes[val] : themes['pleroma-dark']
const bgRgb = hex2rgb(theme[1])
const fgRgb = hex2rgb(theme[2])
const textRgb = hex2rgb(theme[3])
const linkRgb = hex2rgb(theme[4])
+
+ const cRedRgb = hex2rgb(theme[5] || '#FF0000')
+ const cBlueRgb = hex2rgb(theme[6] || '#0000FF')
+ const cGreenRgb = hex2rgb(theme[7] || '#00FF00')
+ const cOrangeRgb = hex2rgb(theme[8] || '#E3FF00')
+
const col = {
bg: bgRgb,
fg: fgRgb,
text: textRgb,
- link: linkRgb
+ link: linkRgb,
+ cRed: cRedRgb,
+ cBlue: cBlueRgb,
+ cGreen: cGreenRgb,
+ cOrange: cOrangeRgb
}
+
// This is a hack, this function is only called during initial load.
// We want to cancel loading the theme from config.json if we're already
// loading a theme from the persisted state.
// Needed some way of dealing with the async way of things.
// load config -> set preset -> wait for styles.json to load ->
// load persisted state -> set colors -> styles.json loaded -> set colors
if (!window.themeLoaded) {
setColors(col, commit)
}
})
}
const StyleSetter = {
setStyle,
setPreset,
setColors
}
export default StyleSetter
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Oct 30, 7:02 AM (22 h, 40 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1799735
Default Alt Text
(87 KB)
Attached To
Mode
rPUFE pleroma-fe-upstream
Attached
Detach File
Event Timeline