Merge branch 'develop' of git.pleroma.social:pleroma/pleroma-fe into develop
This commit is contained in:
commit
763d2c0ce4
32 changed files with 2290 additions and 635 deletions
|
@ -1,7 +1,7 @@
|
|||
# This file is a template, and might need editing before it works on your project.
|
||||
# Official framework image. Look for the different tagged releases at:
|
||||
# https://hub.docker.com/r/library/node/tags/
|
||||
image: node:8
|
||||
image: node:10
|
||||
|
||||
stages:
|
||||
- lint
|
||||
|
@ -14,6 +14,7 @@ lint:
|
|||
script:
|
||||
- yarn
|
||||
- npm run lint
|
||||
- npm run stylelint
|
||||
|
||||
test:
|
||||
stage: test
|
||||
|
|
19
.stylelintrc.json
Normal file
19
.stylelintrc.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"extends": [
|
||||
"stylelint-rscss/config",
|
||||
"stylelint-config-recommended",
|
||||
"stylelint-config-standard"
|
||||
],
|
||||
"rules": {
|
||||
"declaration-no-important": true,
|
||||
"rscss/no-descendant-combinator": false,
|
||||
"rscss/class-format": [
|
||||
true,
|
||||
{
|
||||
"component": "pascal-case",
|
||||
"variant": "^-[a-z]\\w+",
|
||||
"element": "^[a-z]\\w+"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@
|
|||
"unit:watch": "karma start test/unit/karma.conf.js --single-run=false",
|
||||
"e2e": "node test/e2e/runner.js",
|
||||
"test": "npm run unit && npm run e2e",
|
||||
"stylelint": "npx stylelint src/components/status/status.scss",
|
||||
"lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs",
|
||||
"lint-fix": "eslint --fix --ext .js,.vue src test/unit/specs test/e2e/specs"
|
||||
},
|
||||
|
@ -22,8 +23,8 @@
|
|||
"cropperjs": "^1.4.3",
|
||||
"diff": "^3.0.1",
|
||||
"escape-html": "^1.0.3",
|
||||
"parse-link-header": "^1.0.1",
|
||||
"localforage": "^1.5.0",
|
||||
"parse-link-header": "^1.0.1",
|
||||
"phoenix": "^1.3.0",
|
||||
"portal-vue": "^2.1.4",
|
||||
"v-click-outside": "^2.1.1",
|
||||
|
@ -36,7 +37,6 @@
|
|||
"vuex": "^3.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"karma-mocha-reporter": "^2.2.1",
|
||||
"@babel/core": "^7.7.5",
|
||||
"@babel/plugin-transform-runtime": "^7.7.6",
|
||||
"@babel/preset-env": "^7.7.6",
|
||||
|
@ -80,6 +80,7 @@
|
|||
"karma-coverage": "^1.1.1",
|
||||
"karma-firefox-launcher": "^1.1.0",
|
||||
"karma-mocha": "^1.2.0",
|
||||
"karma-mocha-reporter": "^2.2.1",
|
||||
"karma-sinon-chai": "^2.0.2",
|
||||
"karma-sourcemap-loader": "^0.3.7",
|
||||
"karma-spec-reporter": "0.0.26",
|
||||
|
@ -101,6 +102,9 @@
|
|||
"shelljs": "^0.7.4",
|
||||
"sinon": "^2.1.0",
|
||||
"sinon-chai": "^2.8.0",
|
||||
"stylelint": "^13.6.1",
|
||||
"stylelint-config-standard": "^20.0.0",
|
||||
"stylelint-rscss": "^0.4.0",
|
||||
"url-loader": "^1.1.2",
|
||||
"vue-loader": "^14.0.0",
|
||||
"vue-style-loader": "^4.0.0",
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius);
|
||||
}
|
||||
|
||||
.status-body {
|
||||
.StatusContent {
|
||||
img.emoji {
|
||||
width: 1.4em;
|
||||
height: 1.4em;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div
|
||||
class="timeline panel-default"
|
||||
:class="[isExpanded ? 'panel' : 'panel-disabled']"
|
||||
class="Conversation"
|
||||
:class="{ '-expanded' : isExpanded, 'panel' : isExpanded }"
|
||||
>
|
||||
<div
|
||||
v-if="isExpanded"
|
||||
|
@ -28,7 +28,7 @@
|
|||
:replies="getReplies(status.id)"
|
||||
:in-profile="inProfile"
|
||||
:profile-user-id="profileUserId"
|
||||
class="status-fadein panel-body"
|
||||
class="conversation-status status-fadein panel-body"
|
||||
@goto="setHighlight"
|
||||
@toggleExpanded="toggleExpanded"
|
||||
/>
|
||||
|
@ -40,14 +40,27 @@
|
|||
<style lang="scss">
|
||||
@import '../../_variables.scss';
|
||||
|
||||
.timeline {
|
||||
.panel-disabled {
|
||||
.status-el {
|
||||
border-left: none;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
.Conversation {
|
||||
.conversation-status {
|
||||
border-left: none;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: var(--border, $fallback--border);
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
&.-expanded {
|
||||
.conversation-status {
|
||||
border-color: $fallback--border;
|
||||
border-color: var(--border, $fallback--border);
|
||||
border-radius: 0;
|
||||
border-left: 4px solid $fallback--cRed;
|
||||
border-left: 4px solid var(--cRed, $fallback--cRed);
|
||||
}
|
||||
|
||||
.conversation-status:last-child {
|
||||
border-bottom: none;
|
||||
border-radius: 0 0 $fallback--panelRadius $fallback--panelRadius;
|
||||
border-radius: 0 0 var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { timelineNames } from '../timeline_menu/timeline_menu.js'
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
|
||||
const NavPanel = {
|
||||
|
@ -7,9 +8,17 @@ const NavPanel = {
|
|||
}
|
||||
},
|
||||
computed: {
|
||||
onTimelineRoute () {
|
||||
return !!timelineNames()[this.$route.name]
|
||||
},
|
||||
timelinesRoute () {
|
||||
if (this.$store.state.interface.lastTimeline) {
|
||||
return this.$store.state.interface.lastTimeline
|
||||
}
|
||||
return this.currentUser ? 'friends' : 'public-timeline'
|
||||
},
|
||||
...mapState({
|
||||
currentUser: state => state.users.currentUser,
|
||||
chat: state => state.chat.channel,
|
||||
followRequestCount: state => state.api.followRequests.length,
|
||||
privateMode: state => state.instance.private,
|
||||
federating: state => state.instance.federating,
|
||||
|
|
|
@ -2,9 +2,12 @@
|
|||
<div class="nav-panel">
|
||||
<div class="panel panel-default">
|
||||
<ul>
|
||||
<li v-if="currentUser">
|
||||
<router-link :to="{ name: 'friends' }">
|
||||
<i class="button-icon icon-home-2" /> {{ $t("nav.timeline") }}
|
||||
<li v-if="currentUser || !privateMode">
|
||||
<router-link
|
||||
:to="{ name: timelinesRoute }"
|
||||
:class="onTimelineRoute && 'router-link-active'"
|
||||
>
|
||||
<i class="button-icon icon-home-2" /> {{ $t("nav.timelines") }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li v-if="currentUser">
|
||||
|
@ -12,16 +15,6 @@
|
|||
<i class="button-icon icon-bell-alt" /> {{ $t("nav.interactions") }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li v-if="currentUser">
|
||||
<router-link :to="{ name: 'dms', params: { username: currentUser.screen_name } }">
|
||||
<i class="button-icon icon-mail-alt" /> {{ $t("nav.dms") }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li v-if="currentUser">
|
||||
<router-link :to="{ name: 'bookmarks'}">
|
||||
<i class="button-icon icon-bookmark" /> {{ $t("nav.bookmarks") }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li v-if="currentUser && pleromaChatMessagesAvailable">
|
||||
<router-link :to="{ name: 'chats', params: { username: currentUser.screen_name } }">
|
||||
<div
|
||||
|
@ -44,16 +37,6 @@
|
|||
</span>
|
||||
</router-link>
|
||||
</li>
|
||||
<li v-if="currentUser || !privateMode">
|
||||
<router-link :to="{ name: 'public-timeline' }">
|
||||
<i class="button-icon icon-users" /> {{ $t("nav.public_tl") }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li v-if="federating && (currentUser || !privateMode)">
|
||||
<router-link :to="{ name: 'public-external-timeline' }">
|
||||
<i class="button-icon icon-globe" /> {{ $t("nav.twkn") }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link :to="{ name: 'about' }">
|
||||
<i class="button-icon icon-info-circled" /> {{ $t("nav.about") }}
|
||||
|
|
|
@ -60,16 +60,8 @@
|
|||
height: 32px;
|
||||
}
|
||||
|
||||
.status-body {
|
||||
color: $fallback--faint;
|
||||
color: var(--faint, $fallback--faint);
|
||||
a {
|
||||
color: var(--faintLink);
|
||||
}
|
||||
.status-content a {
|
||||
color: var(--postFaintLink);
|
||||
}
|
||||
}
|
||||
--link: var(--faintLink);
|
||||
--text: var(--faint);
|
||||
}
|
||||
|
||||
.follow-request-accept {
|
||||
|
@ -106,7 +98,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
.status-el {
|
||||
/* TODO cleanup this */
|
||||
.Status {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<span class="result-percentage">
|
||||
{{ percentageForOption(option.votes_count) }}%
|
||||
</span>
|
||||
<span v-html="option.title_html"></span>
|
||||
<span v-html="option.title_html" />
|
||||
</div>
|
||||
<div
|
||||
class="result-fill"
|
||||
|
|
|
@ -18,7 +18,9 @@ const Popover = {
|
|||
// Takes a x/y object and tells how many pixels to offset from
|
||||
// anchor point on either axis
|
||||
offset: Object,
|
||||
// Additional styles you may want for the popover container
|
||||
// Replaces the classes you may want for the popover container.
|
||||
// Use 'popover-default' in addition to get the default popover
|
||||
// styles with your custom class.
|
||||
popoverClass: String
|
||||
},
|
||||
data () {
|
||||
|
@ -106,7 +108,7 @@ const Popover = {
|
|||
// single translate or translate3d resulted in blurry text.
|
||||
this.styles = {
|
||||
opacity: 1,
|
||||
transform: `translateX(${Math.floor(translateX)}px) translateY(${Math.floor(translateY)}px)`
|
||||
transform: `translateX(${Math.round(translateX)}px) translateY(${Math.round(translateY)}px)`
|
||||
}
|
||||
},
|
||||
showPopover () {
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
ref="content"
|
||||
:style="styles"
|
||||
class="popover"
|
||||
:class="popoverClass"
|
||||
:class="popoverClass || 'popover-default'"
|
||||
>
|
||||
<slot
|
||||
name="content"
|
||||
|
@ -34,6 +34,9 @@
|
|||
z-index: 8;
|
||||
position: absolute;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.popover-default {
|
||||
transition: opacity 0.3s;
|
||||
|
||||
box-shadow: 1px 1px 4px rgba(0,0,0,.6);
|
||||
|
|
|
@ -13,6 +13,13 @@
|
|||
* - 50px - leaving tiny amount of space so that titlebar + tiny amount of modal is visible
|
||||
*/
|
||||
transform: translateY(calc(((100vh - 100%) / 2 + 100%) - 50px));
|
||||
|
||||
@media all and (max-width: 800px) {
|
||||
/* For mobile, the modal takes 100% of the available screen.
|
||||
This ensures the minimized modal is always 50px above the browser bottom bar regardless of whether or not it is visible.
|
||||
*/
|
||||
transform: translateY(calc(100% - 50px));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,7 +34,7 @@
|
|||
|
||||
@media all and (max-width: 800px) {
|
||||
max-width: 100vw;
|
||||
height: 100vh;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
>.panel-body {
|
||||
|
|
|
@ -49,6 +49,12 @@ const SideDrawer = {
|
|||
federating () {
|
||||
return this.$store.state.instance.federating
|
||||
},
|
||||
timelinesRoute () {
|
||||
if (this.$store.state.interface.lastTimeline) {
|
||||
return this.$store.state.interface.lastTimeline
|
||||
}
|
||||
return this.currentUser ? 'friends' : 'public-timeline'
|
||||
},
|
||||
...mapState({
|
||||
pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable
|
||||
}),
|
||||
|
|
|
@ -39,13 +39,18 @@
|
|||
<i class="button-icon icon-login" /> {{ $t("login.login") }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li
|
||||
v-if="currentUser || !privateMode"
|
||||
@click="toggleDrawer"
|
||||
>
|
||||
<router-link :to="{ name: timelinesRoute }">
|
||||
<i class="button-icon icon-home-2" /> {{ $t("nav.timelines") }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li
|
||||
v-if="currentUser && pleromaChatMessagesAvailable"
|
||||
@click="toggleDrawer"
|
||||
>
|
||||
<router-link :to="{ name: 'dms', params: { username: currentUser.screen_name } }">
|
||||
<i class="button-icon icon-mail-alt" /> {{ $t("nav.dms") }}
|
||||
</router-link>
|
||||
<router-link
|
||||
:to="{ name: 'chats', params: { username: currentUser.screen_name } }"
|
||||
style="position: relative"
|
||||
|
@ -59,34 +64,15 @@
|
|||
</span>
|
||||
</router-link>
|
||||
</li>
|
||||
<li
|
||||
v-if="currentUser"
|
||||
@click="toggleDrawer"
|
||||
>
|
||||
</ul>
|
||||
<ul v-if="currentUser">
|
||||
<li @click="toggleDrawer">
|
||||
<router-link :to="{ name: 'interactions', params: { username: currentUser.screen_name } }">
|
||||
<i class="button-icon icon-bell-alt" /> {{ $t("nav.interactions") }}
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li
|
||||
v-if="currentUser"
|
||||
@click="toggleDrawer"
|
||||
>
|
||||
<router-link :to="{ name: 'friends' }">
|
||||
<i class="button-icon icon-home-2" /> {{ $t("nav.timeline") }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li
|
||||
v-if="currentUser"
|
||||
@click="toggleDrawer"
|
||||
>
|
||||
<router-link :to="{ name: 'bookmarks'}">
|
||||
<i class="button-icon icon-bookmark" /> {{ $t("nav.bookmarks") }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li
|
||||
v-if="currentUser && currentUser.locked"
|
||||
v-if="currentUser.locked"
|
||||
@click="toggleDrawer"
|
||||
>
|
||||
<router-link to="/friend-requests">
|
||||
|
@ -100,19 +86,11 @@
|
|||
</router-link>
|
||||
</li>
|
||||
<li
|
||||
v-if="currentUser || !privateMode"
|
||||
v-if="chat"
|
||||
@click="toggleDrawer"
|
||||
>
|
||||
<router-link to="/main/public">
|
||||
<i class="button-icon icon-users" /> {{ $t("nav.public_tl") }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li
|
||||
v-if="federating && (currentUser || !privateMode)"
|
||||
@click="toggleDrawer"
|
||||
>
|
||||
<router-link to="/main/all">
|
||||
<i class="button-icon icon-globe" /> {{ $t("nav.twkn") }}
|
||||
<router-link :to="{ name: 'chat' }">
|
||||
<i class="button-icon icon-chat" /> {{ $t("nav.chat") }}
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
400
src/components/status/status.scss
Normal file
400
src/components/status/status.scss
Normal file
|
@ -0,0 +1,400 @@
|
|||
|
||||
@import '../../_variables.scss';
|
||||
|
||||
$status-margin: 0.75em;
|
||||
|
||||
.Status {
|
||||
min-width: 0;
|
||||
|
||||
&:hover .avatar {
|
||||
--still-image-img: visible;
|
||||
--still-image-canvas: hidden;
|
||||
}
|
||||
|
||||
&.-focused {
|
||||
background-color: $fallback--lightBg;
|
||||
background-color: var(--selectedPost, $fallback--lightBg);
|
||||
color: $fallback--text;
|
||||
color: var(--selectedPostText, $fallback--text);
|
||||
|
||||
--lightText: var(--selectedPostLightText, $fallback--light);
|
||||
--faint: var(--selectedPostFaintText, $fallback--faint);
|
||||
--faintLink: var(--selectedPostFaintLink, $fallback--faint);
|
||||
--postLink: var(--selectedPostPostLink, $fallback--faint);
|
||||
--postFaintLink: var(--selectedPostFaintPostLink, $fallback--faint);
|
||||
--icon: var(--selectedPostIcon, $fallback--icon);
|
||||
}
|
||||
|
||||
.status-container {
|
||||
display: flex;
|
||||
padding: $status-margin;
|
||||
|
||||
&.-repeat {
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.pin {
|
||||
padding: $status-margin $status-margin 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.left-side {
|
||||
margin-right: $status-margin;
|
||||
}
|
||||
|
||||
.right-side {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.usercard {
|
||||
margin-bottom: $status-margin;
|
||||
}
|
||||
|
||||
.status-username {
|
||||
white-space: nowrap;
|
||||
font-size: 14px;
|
||||
overflow: hidden;
|
||||
max-width: 85%;
|
||||
font-weight: bold;
|
||||
flex-shrink: 1;
|
||||
margin-right: 0.4em;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
.emoji {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
vertical-align: middle;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
.status-favicon {
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
margin-right: 0.4em;
|
||||
}
|
||||
|
||||
.status-heading {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.heading-name-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
line-height: 18px;
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
.account-name {
|
||||
min-width: 1.6em;
|
||||
margin-right: 0.4em;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
flex: 1 1 0;
|
||||
}
|
||||
|
||||
.heading-left {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.heading-right {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.timeago {
|
||||
margin-right: 0.2em;
|
||||
}
|
||||
|
||||
.heading-reply-row {
|
||||
position: relative;
|
||||
align-content: baseline;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
max-width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.reply-to-and-accountname {
|
||||
display: flex;
|
||||
height: 18px;
|
||||
margin-right: 0.5em;
|
||||
max-width: 100%;
|
||||
|
||||
.icon-reply {
|
||||
// mirror the icon
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
}
|
||||
|
||||
& .reply-to-popover,
|
||||
& .reply-to-no-popover {
|
||||
min-width: 0;
|
||||
margin-right: 0.4em;
|
||||
}
|
||||
|
||||
.reply-to-popover {
|
||||
&:hover {
|
||||
border-bottom: 1px solid var(--faint);
|
||||
}
|
||||
|
||||
.faint-link:hover {
|
||||
// override default
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&.-strikethrough {
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid var(--faint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.reply-to {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.reply-to-text {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin-left: 0.2em;
|
||||
}
|
||||
|
||||
.replies-separator {
|
||||
margin-left: 0.4em;
|
||||
}
|
||||
|
||||
.replies {
|
||||
line-height: 18px;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
& > * {
|
||||
margin-right: 0.4em;
|
||||
}
|
||||
}
|
||||
|
||||
.reply-link {
|
||||
height: 17px;
|
||||
}
|
||||
|
||||
.repeat-info {
|
||||
padding: 0.4em $status-margin;
|
||||
line-height: 22px;
|
||||
|
||||
.right-side {
|
||||
display: flex;
|
||||
align-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
i {
|
||||
padding: 0 0.2em;
|
||||
}
|
||||
}
|
||||
|
||||
.repeater-avatar {
|
||||
border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius);
|
||||
margin-left: 28px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.repeater-name {
|
||||
text-overflow: ellipsis;
|
||||
margin-right: 0;
|
||||
|
||||
.emoji {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
vertical-align: middle;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
.status-fadein {
|
||||
animation-duration: 0.4s;
|
||||
animation-name: fadein;
|
||||
}
|
||||
|
||||
@keyframes fadein {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.status-actions {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
margin-top: $status-margin;
|
||||
|
||||
> * {
|
||||
max-width: 4em;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.button-reply {
|
||||
&:not(.-disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:not(.-disabled):hover,
|
||||
&.-active {
|
||||
color: $fallback--cBlue;
|
||||
color: var(--cBlue, $fallback--cBlue);
|
||||
}
|
||||
}
|
||||
|
||||
.muted {
|
||||
padding: 0.25em 0.6em;
|
||||
height: 1.2em;
|
||||
line-height: 1.2em;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
|
||||
& .status-username,
|
||||
& .mute-thread,
|
||||
& .mute-words {
|
||||
word-wrap: normal;
|
||||
word-break: normal;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
& .status-username,
|
||||
& .mute-words {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.status-username {
|
||||
font-weight: normal;
|
||||
flex: 0 1 auto;
|
||||
margin-right: 0.2em;
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
.mute-thread {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.mute-words {
|
||||
flex: 1 0 5em;
|
||||
margin-left: 0.2em;
|
||||
|
||||
&::before {
|
||||
content: ' ';
|
||||
}
|
||||
}
|
||||
|
||||
.unmute {
|
||||
flex: 0 0 auto;
|
||||
margin-left: auto;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.reply-form {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.reply-body {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.favs-repeated-users {
|
||||
margin-top: $status-margin;
|
||||
}
|
||||
|
||||
.stats {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
.avatar-row {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 1px;
|
||||
left: 0;
|
||||
background-color: var(--faint, $fallback--faint);
|
||||
}
|
||||
}
|
||||
|
||||
.stat-count {
|
||||
margin-right: $status-margin;
|
||||
user-select: none;
|
||||
|
||||
.stat-title {
|
||||
color: var(--faint, $fallback--faint);
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-weight: bolder;
|
||||
font-size: 16px;
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
&:hover .stat-title {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-width: 800px) {
|
||||
.repeater-avatar {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.avatar:not(.repeater-avatar) {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
|
||||
// TODO define those other way somehow?
|
||||
// stylelint-disable rscss/class-format
|
||||
&.avatar-compact {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,8 +2,8 @@
|
|||
<!-- eslint-disable vue/no-v-html -->
|
||||
<div
|
||||
v-if="!hideStatus"
|
||||
class="status-el"
|
||||
:class="[{ 'status-el_focused': isFocused }, { 'status-conversation': inlineExpanded }]"
|
||||
class="Status"
|
||||
:class="[{ '-focused': isFocused }, { '-conversation': inlineExpanded }]"
|
||||
>
|
||||
<div
|
||||
v-if="error"
|
||||
|
@ -16,8 +16,8 @@
|
|||
/>
|
||||
</div>
|
||||
<template v-if="muted && !isPreview">
|
||||
<div class="media status container muted">
|
||||
<small class="username">
|
||||
<div class="status-csontainer muted">
|
||||
<small class="status-username">
|
||||
<i
|
||||
v-if="muted && retweet"
|
||||
class="button-icon icon-retweet"
|
||||
|
@ -54,7 +54,7 @@
|
|||
<template v-else>
|
||||
<div
|
||||
v-if="showPinned"
|
||||
class="status-pin"
|
||||
class="pin"
|
||||
>
|
||||
<i class="fa icon-pin faint" />
|
||||
<span class="faint">{{ $t('status.pinned') }}</span>
|
||||
|
@ -63,17 +63,17 @@
|
|||
v-if="retweet && !noHeading && !inConversation"
|
||||
:class="[repeaterClass, { highlighted: repeaterStyle }]"
|
||||
:style="[repeaterStyle]"
|
||||
class="media container retweet-info"
|
||||
class="status-container repeat-info"
|
||||
>
|
||||
<UserAvatar
|
||||
v-if="retweet"
|
||||
class="media-left"
|
||||
class="left-side repeater-avatar"
|
||||
:better-shadow="betterShadow"
|
||||
:user="statusoid.user"
|
||||
/>
|
||||
<div class="media-body faint">
|
||||
<div class="right-side faint">
|
||||
<span
|
||||
class="user-name"
|
||||
class="status-username repeater-name"
|
||||
:title="retweeter"
|
||||
>
|
||||
<router-link
|
||||
|
@ -95,14 +95,14 @@
|
|||
</div>
|
||||
|
||||
<div
|
||||
:class="[userClass, { highlighted: userStyle, 'is-retweet': retweet && !inConversation }]"
|
||||
:class="[userClass, { highlighted: userStyle, '-repeat': retweet && !inConversation }]"
|
||||
:style="[ userStyle ]"
|
||||
class="media status"
|
||||
class="status-container"
|
||||
:data-tags="tags"
|
||||
>
|
||||
<div
|
||||
v-if="!noHeading"
|
||||
class="media-left"
|
||||
class="left-side"
|
||||
>
|
||||
<router-link
|
||||
:to="userProfileLink"
|
||||
|
@ -115,29 +115,29 @@
|
|||
/>
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="status-body">
|
||||
<div class="right-side">
|
||||
<UserCard
|
||||
v-if="userExpanded"
|
||||
:user-id="status.user.id"
|
||||
:rounded="true"
|
||||
:bordered="true"
|
||||
class="status-usercard"
|
||||
class="usercard"
|
||||
/>
|
||||
<div
|
||||
v-if="!noHeading"
|
||||
class="media-heading"
|
||||
class="status-heading"
|
||||
>
|
||||
<div class="heading-name-row">
|
||||
<div class="name-and-account-name">
|
||||
<div class="heading-left">
|
||||
<h4
|
||||
v-if="status.user.name_html"
|
||||
class="user-name"
|
||||
class="status-username"
|
||||
:title="status.user.name"
|
||||
v-html="status.user.name_html"
|
||||
/>
|
||||
<h4
|
||||
v-else
|
||||
class="user-name"
|
||||
class="status-username"
|
||||
:title="status.user.name"
|
||||
>
|
||||
{{ status.user.name }}
|
||||
|
@ -150,8 +150,8 @@
|
|||
{{ status.user.screen_name }}
|
||||
</router-link>
|
||||
<img
|
||||
class="status-favicon"
|
||||
v-if="!!(status.user && status.user.favicon)"
|
||||
class="status-favicon"
|
||||
:src="status.user.favicon"
|
||||
>
|
||||
</div>
|
||||
|
@ -211,6 +211,7 @@
|
|||
:status-id="status.parent_visible && status.in_reply_to_status_id"
|
||||
class="reply-to-popover"
|
||||
style="min-width: 0"
|
||||
:class="{ '-strikethrough': !status.parent_visible }"
|
||||
>
|
||||
<a
|
||||
class="reply-to"
|
||||
|
@ -218,10 +219,9 @@
|
|||
:aria-label="$t('tool_tip.reply')"
|
||||
@click.prevent="gotoOriginal(status.in_reply_to_status_id)"
|
||||
>
|
||||
<i class="button-icon icon-reply" />
|
||||
<i class="button-icon reply-button icon-reply" />
|
||||
<span
|
||||
class="faint-link reply-to-text"
|
||||
:class="{ 'strikethrough': !status.parent_visible }"
|
||||
>
|
||||
{{ $t('status.reply_to') }}
|
||||
</span>
|
||||
|
@ -229,7 +229,7 @@
|
|||
</StatusPopover>
|
||||
<span
|
||||
v-else
|
||||
class="reply-to"
|
||||
class="reply-to-no-popover"
|
||||
>
|
||||
<span class="reply-to-text">{{ $t('status.reply_to') }}</span>
|
||||
</span>
|
||||
|
@ -317,19 +317,19 @@
|
|||
|
||||
<div
|
||||
v-if="!noHeading && !isPreview"
|
||||
class="status-actions media-body"
|
||||
class="status-actions"
|
||||
>
|
||||
<div>
|
||||
<i
|
||||
v-if="loggedIn"
|
||||
class="button-icon icon-reply"
|
||||
class="button-icon button-reply icon-reply"
|
||||
:title="$t('tool_tip.reply')"
|
||||
:class="{'button-icon-active': replying}"
|
||||
:class="{'-active': replying}"
|
||||
@click.prevent="toggleReplying"
|
||||
/>
|
||||
<i
|
||||
v-else
|
||||
class="button-icon button-icon-disabled icon-reply"
|
||||
class="button-icon button-reply -disabled icon-reply"
|
||||
:title="$t('tool_tip.reply')"
|
||||
/>
|
||||
<span v-if="status.replies_count > 0">{{ status.replies_count }}</span>
|
||||
|
@ -357,7 +357,7 @@
|
|||
</div>
|
||||
<div
|
||||
v-if="replying"
|
||||
class="container"
|
||||
class="status-container reply-form"
|
||||
>
|
||||
<PostStatusForm
|
||||
class="reply-body"
|
||||
|
@ -375,439 +375,4 @@
|
|||
</template>
|
||||
|
||||
<script src="./status.js" ></script>
|
||||
<style lang="scss">
|
||||
@import '../../_variables.scss';
|
||||
|
||||
$status-margin: 0.75em;
|
||||
|
||||
.status-body {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.status-pin {
|
||||
padding: $status-margin $status-margin 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.media-left {
|
||||
margin-right: $status-margin;
|
||||
}
|
||||
|
||||
.status-el {
|
||||
border-left-width: 0px;
|
||||
min-width: 0;
|
||||
border-color: $fallback--border;
|
||||
border-color: var(--border, $fallback--border);
|
||||
|
||||
border-left: 4px $fallback--cRed;
|
||||
border-left: 4px var(--cRed, $fallback--cRed);
|
||||
|
||||
&_focused {
|
||||
background-color: $fallback--lightBg;
|
||||
background-color: var(--selectedPost, $fallback--lightBg);
|
||||
color: $fallback--text;
|
||||
color: var(--selectedPostText, $fallback--text);
|
||||
--lightText: var(--selectedPostLightText, $fallback--light);
|
||||
--faint: var(--selectedPostFaintText, $fallback--faint);
|
||||
--faintLink: var(--selectedPostFaintLink, $fallback--faint);
|
||||
--postLink: var(--selectedPostPostLink, $fallback--faint);
|
||||
--postFaintLink: var(--selectedPostFaintPostLink, $fallback--faint);
|
||||
--icon: var(--selectedPostIcon, $fallback--icon);
|
||||
}
|
||||
|
||||
.timeline & {
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
|
||||
.media-body {
|
||||
flex: 1;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.status-usercard {
|
||||
margin-bottom: $status-margin;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
white-space: nowrap;
|
||||
font-size: 14px;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
max-width: 85%;
|
||||
font-weight: bold;
|
||||
|
||||
img.emoji {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
vertical-align: middle;
|
||||
object-fit: contain
|
||||
}
|
||||
}
|
||||
|
||||
.status-favicon {
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
margin-right: 0.4em;
|
||||
}
|
||||
|
||||
.media-heading {
|
||||
padding: 0;
|
||||
vertical-align: bottom;
|
||||
flex-basis: 100%;
|
||||
margin-bottom: 0.5em;
|
||||
|
||||
small {
|
||||
font-weight: lighter;
|
||||
}
|
||||
|
||||
.heading-name-row {
|
||||
padding: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
line-height: 18px;
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.name-and-account-name {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
flex-shrink: 1;
|
||||
margin-right: 0.4em;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.account-name {
|
||||
min-width: 1.6em;
|
||||
margin-right: 0.4em;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
flex: 1 1 0;
|
||||
}
|
||||
}
|
||||
|
||||
.heading-right {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.timeago {
|
||||
margin-right: 0.2em;
|
||||
}
|
||||
|
||||
.heading-reply-row {
|
||||
position: relative;
|
||||
align-content: baseline;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
max-width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: stretch;
|
||||
|
||||
> .reply-to-and-accountname > a {
|
||||
overflow: hidden;
|
||||
max-width: 100%;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
.reply-to-and-accountname {
|
||||
display: flex;
|
||||
height: 18px;
|
||||
margin-right: 0.5em;
|
||||
max-width: 100%;
|
||||
.icon-reply {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
}
|
||||
|
||||
.reply-info {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.reply-to-popover {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.reply-to {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.reply-to-text {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin: 0 0.4em 0 0.2em;
|
||||
}
|
||||
|
||||
.strikethrough {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.replies-separator {
|
||||
margin-left: 0.4em;
|
||||
}
|
||||
|
||||
.replies {
|
||||
line-height: 18px;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
& > * {
|
||||
margin-right: 0.4em;
|
||||
}
|
||||
}
|
||||
|
||||
.reply-link {
|
||||
height: 17px;
|
||||
}
|
||||
}
|
||||
|
||||
.retweet-info {
|
||||
padding: 0.4em $status-margin;
|
||||
margin: 0;
|
||||
|
||||
.avatar.still-image {
|
||||
border-radius: $fallback--avatarAltRadius;
|
||||
border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius);
|
||||
margin-left: 28px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.media-body {
|
||||
font-size: 1em;
|
||||
line-height: 22px;
|
||||
|
||||
display: flex;
|
||||
align-content: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.user-name {
|
||||
font-weight: bold;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
img {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
vertical-align: middle;
|
||||
object-fit: contain
|
||||
}
|
||||
}
|
||||
|
||||
i {
|
||||
padding: 0 0.2em;
|
||||
}
|
||||
|
||||
a {
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.status-fadein {
|
||||
animation-duration: 0.4s;
|
||||
animation-name: fadein;
|
||||
}
|
||||
|
||||
@keyframes fadein {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.status-conversation {
|
||||
border-left-style: solid;
|
||||
}
|
||||
|
||||
.status-actions {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
margin-top: $status-margin;
|
||||
|
||||
> * {
|
||||
max-width: 4em;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.button-icon.icon-reply {
|
||||
&:not(.button-icon-disabled):hover,
|
||||
&.button-icon-active {
|
||||
color: $fallback--cBlue;
|
||||
color: var(--cBlue, $fallback--cBlue);
|
||||
}
|
||||
}
|
||||
|
||||
.button-icon.icon-reply {
|
||||
&:not(.button-icon-disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.status:hover .animated.avatar {
|
||||
canvas {
|
||||
display: none;
|
||||
}
|
||||
img {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
.status {
|
||||
display: flex;
|
||||
padding: $status-margin;
|
||||
&.is-retweet {
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.status-conversation:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.muted {
|
||||
padding: .25em .6em;
|
||||
height: 1.2em;
|
||||
line-height: 1.2em;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
|
||||
.username, .mute-thread, .mute-words {
|
||||
word-wrap: normal;
|
||||
word-break: normal;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.username, .mute-words {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.username {
|
||||
flex: 0 1 auto;
|
||||
margin-right: .2em;
|
||||
}
|
||||
|
||||
.mute-thread {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.mute-words {
|
||||
flex: 1 0 5em;
|
||||
margin-left: .2em;
|
||||
&::before {
|
||||
content: ' '
|
||||
}
|
||||
}
|
||||
|
||||
.unmute {
|
||||
flex: 0 0 auto;
|
||||
margin-left: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.reply-body {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.favs-repeated-users {
|
||||
margin-top: $status-margin;
|
||||
|
||||
.stats {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
line-height: 1em;
|
||||
|
||||
.stat-count {
|
||||
margin-right: $status-margin;
|
||||
user-select: none;
|
||||
|
||||
&:hover .stat-title {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.stat-title {
|
||||
color: var(--faint, $fallback--faint);
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-weight: bolder;
|
||||
font-size: 16px;
|
||||
line-height: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-row {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 1px;
|
||||
left: 0;
|
||||
background-color: var(--faint, $fallback--faint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-width: 800px) {
|
||||
.status-el {
|
||||
.retweet-info {
|
||||
.avatar.still-image {
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.status {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.status .avatar.still-image {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
|
||||
&.avatar-compact {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
<style src="./status.scss" lang="scss"></style>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<div class="status-body">
|
||||
<div class="StatusContent">
|
||||
<slot name="header" />
|
||||
<div
|
||||
v-if="status.summary_html"
|
||||
|
@ -133,7 +133,7 @@
|
|||
|
||||
$status-margin: 0.75em;
|
||||
|
||||
.status-body {
|
||||
.StatusContent {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
|
@ -275,6 +275,7 @@ $status-margin: 0.75em;
|
|||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
height: 1.4em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -283,13 +284,4 @@ $status-margin: 0.75em;
|
|||
color: $fallback--cGreen;
|
||||
color: var(--postGreentext, $fallback--cGreen);
|
||||
}
|
||||
|
||||
.timeline :not(.panel-disabled) > {
|
||||
.status-el:last-child {
|
||||
border-radius: 0 0 $fallback--panelRadius $fallback--panelRadius;
|
||||
border-radius: 0 0 var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius);
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<Popover
|
||||
trigger="hover"
|
||||
popover-class="status-popover"
|
||||
popover-class="popover-default status-popover"
|
||||
:bound-to="{ x: 'container' }"
|
||||
@show="enter"
|
||||
>
|
||||
|
@ -52,7 +52,8 @@
|
|||
box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5);
|
||||
box-shadow: var(--popupShadow);
|
||||
|
||||
.status-el.status-el {
|
||||
/* TODO cleanup this */
|
||||
.Status.Status {
|
||||
border: none;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,43 +35,6 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&:hover canvas {
|
||||
display: none;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
&.animated {
|
||||
&:hover::before,
|
||||
img {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
&:hover img {
|
||||
visibility: visible
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: 'gif';
|
||||
position: absolute;
|
||||
line-height: 10px;
|
||||
font-size: 10px;
|
||||
top: 5px;
|
||||
left: 5px;
|
||||
background: rgba(127,127,127,.5);
|
||||
color: #FFF;
|
||||
display: block;
|
||||
padding: 2px 4px;
|
||||
border-radius: $fallback--tooltipRadius;
|
||||
border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
|
||||
canvas {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
@ -81,6 +44,44 @@
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
visibility: var(--still-image-canvas, visible);
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
&.animated {
|
||||
&::before {
|
||||
content: 'gif';
|
||||
position: absolute;
|
||||
line-height: 10px;
|
||||
font-size: 10px;
|
||||
top: 5px;
|
||||
left: 5px;
|
||||
background: rgba(127, 127, 127, 0.5);
|
||||
color: #fff;
|
||||
display: block;
|
||||
padding: 2px 4px;
|
||||
border-radius: $fallback--tooltipRadius;
|
||||
border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&:hover canvas {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover::before,
|
||||
img {
|
||||
visibility: var(--still-image-img, hidden);
|
||||
}
|
||||
|
||||
&:hover img {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import Vue from 'vue'
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
import './tab_switcher.scss'
|
||||
|
||||
|
@ -44,7 +45,13 @@ export default Vue.component('tab-switcher', {
|
|||
} else {
|
||||
return this.active
|
||||
}
|
||||
}
|
||||
},
|
||||
settingsModalVisible () {
|
||||
return this.settingsModalState === 'visible'
|
||||
},
|
||||
...mapState({
|
||||
settingsModalState: state => state.interface.settingsModalState
|
||||
})
|
||||
},
|
||||
beforeUpdate () {
|
||||
const currentSlot = this.$slots.default[this.active]
|
||||
|
@ -134,7 +141,7 @@ export default Vue.component('tab-switcher', {
|
|||
<div class="tabs">
|
||||
{tabs}
|
||||
</div>
|
||||
<div ref="contents" class={'contents' + (this.scrollableTabs ? ' scrollable-tabs' : '')}>
|
||||
<div ref="contents" class={'contents' + (this.scrollableTabs ? ' scrollable-tabs' : '')} v-body-scroll-lock={this.settingsModalVisible}>
|
||||
{contents}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import Status from '../status/status.vue'
|
||||
import timelineFetcher from '../../services/timeline_fetcher/timeline_fetcher.service.js'
|
||||
import Conversation from '../conversation/conversation.vue'
|
||||
import TimelineMenu from '../timeline_menu/timeline_menu.vue'
|
||||
import { throttle, keyBy } from 'lodash'
|
||||
|
||||
export const getExcludedStatusIdsByPinning = (statuses, pinnedStatusIds) => {
|
||||
|
@ -35,6 +36,11 @@ const Timeline = {
|
|||
bottomedOut: false
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Status,
|
||||
Conversation,
|
||||
TimelineMenu
|
||||
},
|
||||
computed: {
|
||||
timelineError () {
|
||||
return this.$store.state.statuses.error
|
||||
|
@ -74,10 +80,6 @@ const Timeline = {
|
|||
return keyBy(this.pinnedStatusIds)
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Status,
|
||||
Conversation
|
||||
},
|
||||
created () {
|
||||
const store = this.$store
|
||||
const credentials = store.state.users.currentUser.credentials
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
<template>
|
||||
<div :class="classes.root">
|
||||
<div :class="[classes.root, 'timeline']">
|
||||
<div :class="classes.header">
|
||||
<div class="title">
|
||||
{{ title }}
|
||||
</div>
|
||||
<TimelineMenu v-if="!embedded" />
|
||||
<div
|
||||
v-if="timelineError"
|
||||
class="loadmore-error alert error"
|
||||
|
@ -106,4 +104,16 @@
|
|||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.timeline-heading {
|
||||
max-width: 100%;
|
||||
flex-wrap: nowrap;
|
||||
.loadmore-button {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.loadmore-text {
|
||||
flex-shrink: 0;
|
||||
line-height: 1em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
57
src/components/timeline_menu/timeline_menu.js
Normal file
57
src/components/timeline_menu/timeline_menu.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
import Popover from '../popover/popover.vue'
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
// Route -> i18n key mapping, exported andnot in the computed
|
||||
// because nav panel benefits from the same information.
|
||||
export const timelineNames = () => {
|
||||
return {
|
||||
'friends': 'nav.timeline',
|
||||
'bookmarks': 'nav.bookmarks',
|
||||
'dms': 'nav.dms',
|
||||
'public-timeline': 'nav.public_tl',
|
||||
'public-external-timeline': 'nav.twkn'
|
||||
}
|
||||
}
|
||||
|
||||
const TimelineMenu = {
|
||||
components: {
|
||||
Popover
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
isOpen: false
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if (this.currentUser && this.currentUser.locked) {
|
||||
this.$store.dispatch('startFetchingFollowRequests')
|
||||
}
|
||||
if (timelineNames()[this.$route.name]) {
|
||||
this.$store.dispatch('setLastTimeline', this.$route.name)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openMenu () {
|
||||
// $nextTick is too fast, animation won't play back but
|
||||
// instead starts in fully open position. Low values
|
||||
// like 1-5 work on fast machines but not on mobile, 25
|
||||
// seems like a good compromise that plays without significant
|
||||
// added lag.
|
||||
setTimeout(() => {
|
||||
this.isOpen = true
|
||||
}, 25)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
currentUser: state => state.users.currentUser,
|
||||
privateMode: state => state.instance.private,
|
||||
federating: state => state.instance.federating
|
||||
}),
|
||||
timelineNames () {
|
||||
return timelineNames()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default TimelineMenu
|
180
src/components/timeline_menu/timeline_menu.vue
Normal file
180
src/components/timeline_menu/timeline_menu.vue
Normal file
|
@ -0,0 +1,180 @@
|
|||
<template>
|
||||
<Popover
|
||||
trigger="click"
|
||||
class="timeline-menu"
|
||||
:class="{ 'open': isOpen }"
|
||||
:margin="{ left: -15, right: -200 }"
|
||||
:bound-to="{ x: 'container' }"
|
||||
popover-class="timeline-menu-popover-wrap"
|
||||
@show="openMenu"
|
||||
@close="() => isOpen = false"
|
||||
>
|
||||
<div
|
||||
slot="content"
|
||||
class="timeline-menu-popover panel panel-default"
|
||||
>
|
||||
<ul>
|
||||
<li v-if="currentUser">
|
||||
<router-link :to="{ name: 'friends' }">
|
||||
<i class="button-icon icon-home-2" />{{ $t("nav.timeline") }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li v-if="currentUser">
|
||||
<router-link :to="{ name: 'bookmarks'}">
|
||||
<i class="button-icon icon-bookmark" />{{ $t("nav.bookmarks") }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li v-if="currentUser">
|
||||
<router-link :to="{ name: 'dms', params: { username: currentUser.screen_name } }">
|
||||
<i class="button-icon icon-mail-alt" />{{ $t("nav.dms") }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li v-if="currentUser || !privateMode">
|
||||
<router-link :to="{ name: 'public-timeline' }">
|
||||
<i class="button-icon icon-users" />{{ $t("nav.public_tl") }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li v-if="federating && (currentUser || !privateMode)">
|
||||
<router-link :to="{ name: 'public-external-timeline' }">
|
||||
<i class="button-icon icon-globe" />{{ $t("nav.twkn") }}
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div
|
||||
slot="trigger"
|
||||
class="title timeline-menu-title"
|
||||
>
|
||||
<span>{{ $t(timelineNames[$route.name]) }}</span>
|
||||
<i class="icon-down-open" />
|
||||
</div>
|
||||
</Popover>
|
||||
</template>
|
||||
|
||||
<script src="./timeline_menu.js" ></script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../../_variables.scss';
|
||||
|
||||
.timeline-menu {
|
||||
flex-shrink: 1;
|
||||
margin-right: auto;
|
||||
min-width: 0;
|
||||
width: 24rem;
|
||||
.timeline-menu-popover-wrap {
|
||||
overflow: hidden;
|
||||
// Match panel heading padding to line up menu with bottom of heading
|
||||
margin-top: 0.6rem;
|
||||
padding: 0 15px 15px 15px;
|
||||
}
|
||||
.timeline-menu-popover {
|
||||
width: 24rem;
|
||||
max-width: 100vw;
|
||||
margin: 0;
|
||||
font-size: 1rem;
|
||||
border-top-right-radius: 0;
|
||||
border-top-left-radius: 0;
|
||||
transform: translateY(-100%);
|
||||
transition: transform 100ms;
|
||||
}
|
||||
.panel::after {
|
||||
border-top-right-radius: 0;
|
||||
border-top-left-radius: 0;
|
||||
}
|
||||
&.open .timeline-menu-popover {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.timeline-menu-title {
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
user-select: none;
|
||||
width: 100%;
|
||||
|
||||
span {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
i {
|
||||
margin-left: 0.6em;
|
||||
flex-shrink: 0;
|
||||
font-size: 1rem;
|
||||
transition: transform 100ms;
|
||||
}
|
||||
}
|
||||
|
||||
&.open .timeline-menu-title i {
|
||||
color: $fallback--text;
|
||||
color: var(--panelText, $fallback--text);
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.panel {
|
||||
box-shadow: var(--popoverShadow);
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
border-bottom: 1px solid;
|
||||
border-color: $fallback--border;
|
||||
border-color: var(--border, $fallback--border);
|
||||
padding: 0;
|
||||
|
||||
&:last-child a {
|
||||
border-bottom-right-radius: $fallback--panelRadius;
|
||||
border-bottom-right-radius: var(--panelRadius, $fallback--panelRadius);
|
||||
border-bottom-left-radius: $fallback--panelRadius;
|
||||
border-bottom-left-radius: var(--panelRadius, $fallback--panelRadius);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border: none;
|
||||
}
|
||||
|
||||
i {
|
||||
margin: 0 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
display: block;
|
||||
padding: 0.6em 0;
|
||||
|
||||
&:hover {
|
||||
background-color: $fallback--lightBg;
|
||||
background-color: var(--selectedMenu, $fallback--lightBg);
|
||||
color: $fallback--link;
|
||||
color: var(--selectedMenuText, $fallback--link);
|
||||
--faint: var(--selectedMenuFaintText, $fallback--faint);
|
||||
--faintLink: var(--selectedMenuFaintLink, $fallback--faint);
|
||||
--lightText: var(--selectedMenuLightText, $fallback--lightText);
|
||||
--icon: var(--selectedMenuIcon, $fallback--icon);
|
||||
}
|
||||
|
||||
&.router-link-active {
|
||||
font-weight: bolder;
|
||||
background-color: $fallback--lightBg;
|
||||
background-color: var(--selectedMenu, $fallback--lightBg);
|
||||
color: $fallback--text;
|
||||
color: var(--selectedMenuText, $fallback--text);
|
||||
--faint: var(--selectedMenuFaintText, $fallback--faint);
|
||||
--faintLink: var(--selectedMenuFaintLink, $fallback--faint);
|
||||
--lightText: var(--selectedMenuLightText, $fallback--lightText);
|
||||
--icon: var(--selectedMenuIcon, $fallback--icon);
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
|
@ -20,13 +20,14 @@
|
|||
:key="index"
|
||||
class="user-profile-field"
|
||||
>
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<dt
|
||||
:title="user.fields_text[index].name"
|
||||
class="user-profile-field-name"
|
||||
@click.prevent="linkClicked"
|
||||
v-html="field.name"
|
||||
/>
|
||||
>
|
||||
{{ field.name }}
|
||||
</dt>
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<dd
|
||||
:title="user.fields_text[index].value"
|
||||
class="user-profile-field-value"
|
||||
|
|
|
@ -146,7 +146,8 @@
|
|||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
> .status-el {
|
||||
/* TODO cleanup this */
|
||||
> .Status {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
"dms": "Direktnachrichten",
|
||||
"public_tl": "Öffentliche Zeitleiste",
|
||||
"timeline": "Zeitleiste",
|
||||
"twkn": "Das gesamte bekannte Netzwerk",
|
||||
"twkn": "Bekannte Netzwerk",
|
||||
"user_search": "Benutzersuche",
|
||||
"search": "Suche",
|
||||
"preferences": "Voreinstellungen",
|
||||
|
|
|
@ -120,12 +120,13 @@
|
|||
"dms": "Direct Messages",
|
||||
"public_tl": "Public Timeline",
|
||||
"timeline": "Timeline",
|
||||
"twkn": "The Whole Known Network",
|
||||
"twkn": "Known Network",
|
||||
"bookmarks": "Bookmarks",
|
||||
"user_search": "User Search",
|
||||
"search": "Search",
|
||||
"who_to_follow": "Who to follow",
|
||||
"preferences": "Preferences",
|
||||
"timelines": "Timelines",
|
||||
"chats": "Chats"
|
||||
},
|
||||
"notifications": {
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
"dms": "Yksityisviestit",
|
||||
"public_tl": "Julkinen Aikajana",
|
||||
"timeline": "Aikajana",
|
||||
"twkn": "Koko Tunnettu Verkosto",
|
||||
"twkn": "Tunnettu Verkosto",
|
||||
"user_search": "Käyttäjähaku",
|
||||
"who_to_follow": "Seurausehdotukset",
|
||||
"preferences": "Asetukset",
|
||||
|
|
|
@ -16,7 +16,8 @@ const defaultState = {
|
|||
},
|
||||
mobileLayout: false,
|
||||
globalNotices: [],
|
||||
layoutHeight: 0
|
||||
layoutHeight: 0,
|
||||
lastTimeline: null
|
||||
}
|
||||
|
||||
const interfaceMod = {
|
||||
|
@ -69,6 +70,9 @@ const interfaceMod = {
|
|||
},
|
||||
setLayoutHeight (state, value) {
|
||||
state.layoutHeight = value
|
||||
},
|
||||
setLastTimeline (state, value) {
|
||||
state.lastTimeline = value
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
|
@ -117,6 +121,9 @@ const interfaceMod = {
|
|||
},
|
||||
setLayoutHeight ({ commit }, value) {
|
||||
commit('setLayoutHeight', value)
|
||||
},
|
||||
setLastTimeline ({ commit }, value) {
|
||||
commit('setLastTimeline', value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -509,6 +509,7 @@ const users = {
|
|||
store.commit('clearNotifications')
|
||||
store.commit('resetStatuses')
|
||||
store.dispatch('resetChats')
|
||||
store.dispatch('setLastTimeline', 'public-timeline')
|
||||
})
|
||||
},
|
||||
loginUser (store, accessToken) {
|
||||
|
|
Loading…
Reference in a new issue