Merge remote-tracking branch 'upstream/develop' into shigusegubu

* upstream/develop: (27 commits)
  remove needless ref
  show preview popover when hover numbered replies
  refactor conditions
  do not make too many nested div
  add fetchStatus action
  refactor status loading logic
  split status preview popover into a separate component
  uninstall mobile-detect library
  listen both events
  minor css fix
  restrict distance at top side only
  set different trigger event in desktop and mobile by default
  fix eslint warnings
  fix popper go behind the top bar
  migrate Popper to v-popover
  fix popper go behind the top bar
  fix eslint warnings
  reset font-size to normal text size using rem
  use top placement by default
  hide status preview popper when hover popper content
  ...
This commit is contained in:
Henry Jameson 2019-10-29 09:22:14 +02:00
commit 4f72a995a2
12 changed files with 210 additions and 153 deletions

View file

@ -39,10 +39,13 @@ h4 {
text-align: center; text-align: center;
} }
html {
font-size: 14px;
}
body { body {
font-family: sans-serif; font-family: sans-serif;
font-family: var(--interfaceFont, sans-serif); font-family: var(--interfaceFont, sans-serif);
font-size: 14px;
margin: 0; margin: 0;
color: $fallback--text; color: $fallback--text;
color: var(--text, $fallback--text); color: var(--text, $fallback--text);

View file

@ -4,8 +4,6 @@
trigger="click" trigger="click"
placement="top" placement="top"
class="extra-button-popover" class="extra-button-popover"
:offset="5"
:container="false"
> >
<div slot="popover"> <div slot="popover">
<div class="dropdown-menu"> <div class="dropdown-menu">

View file

@ -3,9 +3,7 @@
<v-popover <v-popover
trigger="click" trigger="click"
class="moderation-tools-popover" class="moderation-tools-popover"
:container="false"
placement="bottom-end" placement="bottom-end"
:offset="5"
@show="showDropDown = true" @show="showDropDown = true"
@hide="showDropDown = false" @hide="showDropDown = false"
> >

View file

@ -20,7 +20,6 @@
margin: 5px; margin: 5px;
border-color: $fallback--bg; border-color: $fallback--bg;
border-color: var(--bg, $fallback--bg); border-color: var(--bg, $fallback--bg);
z-index: 1;
} }
&[x-placement^="top"] { &[x-placement^="top"] {
@ -31,7 +30,7 @@
border-left-color: transparent !important; border-left-color: transparent !important;
border-right-color: transparent !important; border-right-color: transparent !important;
border-bottom-color: transparent !important; border-bottom-color: transparent !important;
bottom: -5px; bottom: -4px;
left: calc(50% - 5px); left: calc(50% - 5px);
margin-top: 0; margin-top: 0;
margin-bottom: 0; margin-bottom: 0;
@ -46,7 +45,7 @@
border-left-color: transparent !important; border-left-color: transparent !important;
border-right-color: transparent !important; border-right-color: transparent !important;
border-top-color: transparent !important; border-top-color: transparent !important;
top: -5px; top: -4px;
left: calc(50% - 5px); left: calc(50% - 5px);
margin-top: 0; margin-top: 0;
margin-bottom: 0; margin-bottom: 0;
@ -61,7 +60,7 @@
border-left-color: transparent !important; border-left-color: transparent !important;
border-top-color: transparent !important; border-top-color: transparent !important;
border-bottom-color: transparent !important; border-bottom-color: transparent !important;
left: -5px; left: -4px;
top: calc(50% - 5px); top: calc(50% - 5px);
margin-left: 0; margin-left: 0;
margin-right: 0; margin-right: 0;
@ -76,7 +75,7 @@
border-top-color: transparent !important; border-top-color: transparent !important;
border-right-color: transparent !important; border-right-color: transparent !important;
border-bottom-color: transparent !important; border-bottom-color: transparent !important;
right: -5px; right: -4px;
top: calc(50% - 5px); top: calc(50% - 5px);
margin-left: 0; margin-left: 0;
margin-right: 0; margin-right: 0;

View file

@ -10,11 +10,12 @@ import Gallery from '../gallery/gallery.vue'
import LinkPreview from '../link-preview/link-preview.vue' import LinkPreview from '../link-preview/link-preview.vue'
import AvatarList from '../avatar_list/avatar_list.vue' import AvatarList from '../avatar_list/avatar_list.vue'
import Timeago from '../timeago/timeago.vue' import Timeago from '../timeago/timeago.vue'
import StatusPopover from '../status_popover/status_popover.vue'
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
import fileType from 'src/services/file_type/file_type.service' import fileType from 'src/services/file_type/file_type.service'
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js' import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
import { mentionMatchesUrl, extractTagFromUrl } from 'src/services/matcher/matcher.service.js' import { mentionMatchesUrl, extractTagFromUrl } from 'src/services/matcher/matcher.service.js'
import { filter, find, unescape, uniqBy } from 'lodash' import { filter, unescape, uniqBy } from 'lodash'
import { mapGetters } from 'vuex' import { mapGetters } from 'vuex'
const Status = { const Status = {
@ -38,8 +39,6 @@ const Status = {
replying: false, replying: false,
unmuted: false, unmuted: false,
userExpanded: false, userExpanded: false,
preview: null,
showPreview: false,
showingTall: this.inConversation && this.focused, showingTall: this.inConversation && this.focused,
showingLongSubject: false, showingLongSubject: false,
error: null, error: null,
@ -293,7 +292,8 @@ const Status = {
Gallery, Gallery,
LinkPreview, LinkPreview,
AvatarList, AvatarList,
Timeago Timeago,
StatusPopover
}, },
methods: { methods: {
visibilityIcon (visibility) { visibilityIcon (visibility) {
@ -368,27 +368,6 @@ const Status = {
this.expandingSubject = true this.expandingSubject = true
} }
}, },
replyEnter (id, event) {
this.showPreview = true
const targetId = id
const statuses = this.$store.state.statuses.allStatuses
if (!this.preview) {
// if we have the status somewhere already
this.preview = find(statuses, { 'id': targetId })
// or if we have to fetch it
if (!this.preview) {
this.$store.state.api.backendInteractor.fetchStatus({ id }).then((status) => {
this.preview = status
})
}
} else if (this.preview.id !== targetId) {
this.preview = find(statuses, { 'id': targetId })
}
},
replyLeave () {
this.showPreview = false
},
generateUserProfileLink (id, name) { generateUserProfileLink (id, name) {
return generateProfileLink(id, name, this.$store.state.instance.restrictedNicknames) return generateProfileLink(id, name, this.$store.state.instance.restrictedNicknames)
}, },

View file

@ -174,20 +174,26 @@
v-if="isReply" v-if="isReply"
class="reply-to-and-accountname" class="reply-to-and-accountname"
> >
<a <StatusPopover
class="reply-to" v-if="!isPreview"
href="#" :status-id="status.in_reply_to_status_id"
:aria-label="$t('tool_tip.reply')"
@click.prevent="gotoOriginal(status.in_reply_to_status_id)"
@mouseenter.prevent.stop="replyEnter(status.in_reply_to_status_id, $event)"
@mouseleave.prevent.stop="replyLeave()"
> >
<i <a
v-if="!isPreview" class="reply-to"
class="button-icon icon-reply" href="#"
/> :aria-label="$t('tool_tip.reply')"
<span class="faint-link reply-to-text">{{ $t('status.reply_to') }}</span> @click.prevent="gotoOriginal(status.in_reply_to_status_id)"
</a> >
<i class="button-icon icon-reply" />
<span class="faint-link reply-to-text">{{ $t('status.reply_to') }}</span>
</a>
</StatusPopover>
<span
v-else
class="reply-to"
>
<span class="reply-to-text">{{ $t('status.reply_to') }}</span>
</span>
<router-link :to="replyProfileLink"> <router-link :to="replyProfileLink">
{{ replyToName }} {{ replyToName }}
</router-link> </router-link>
@ -199,50 +205,25 @@
</span> </span>
</div> </div>
<div <div
v-if="inConversation && !isPreview" v-if="inConversation && !isPreview && replies && replies.length"
class="replies" class="replies"
> >
<span <span class="faint">{{ $t('status.replies_list') }}</span>
v-if="replies && replies.length" <StatusPopover
class="faint" v-for="reply in replies"
>{{ $t('status.replies_list') }}</span> :key="reply.id"
<template v-if="replies"> :status-id="reply.id"
<span >
v-for="reply in replies" <a
:key="reply.id" href="#"
class="reply-link faint" class="reply-link"
> @click.prevent="gotoOriginal(reply.id)"
<a >{{ reply.name }}</a>
href="#" </StatusPopover>
@click.prevent="gotoOriginal(reply.id)"
@mouseenter="replyEnter(reply.id, $event)"
@mouseout="replyLeave()"
>{{ reply.name }}</a>
</span>
</template>
</div> </div>
</div> </div>
</div> </div>
<div
v-if="showPreview"
class="status-preview-container"
>
<status
v-if="preview"
class="status-preview"
:is-preview="true"
:statusoid="preview"
:compact="true"
/>
<div
v-else
class="status-preview status-preview-loading"
>
<i class="icon-spin4 animate-spin" />
</div>
</div>
<div <div
v-if="longSubject" v-if="longSubject"
class="status-content-wrapper" class="status-content-wrapper"
@ -439,18 +420,6 @@ $status-margin: 0.75em;
min-width: 0; min-width: 0;
} }
.status-preview.status-el {
border-style: solid;
border-width: 1px;
border-color: $fallback--border;
border-color: var(--border, $fallback--border);
}
.status-preview-container {
position: relative;
max-width: 100%;
}
.status-pin { .status-pin {
padding: $status-margin $status-margin 0; padding: $status-margin $status-margin 0;
display: flex; display: flex;
@ -458,44 +427,6 @@ $status-margin: 0.75em;
justify-content: flex-end; justify-content: flex-end;
} }
.status-preview {
position: absolute;
max-width: 95%;
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: $fallback--tooltipRadius;
border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5);
box-shadow: var(--popupShadow);
margin-top: 0.25em;
margin-left: 0.5em;
z-index: 50;
.status {
flex: 1;
border: 0;
min-width: 15em;
}
}
.status-preview-loading {
display: block;
min-width: 15em;
padding: 1em;
text-align: center;
border-width: 1px;
border-style: solid;
i {
font-size: 2em;
}
}
.media-left { .media-left {
margin-right: $status-margin; margin-right: $status-margin;
} }
@ -553,11 +484,6 @@ $status-margin: 0.75em;
flex-basis: 100%; flex-basis: 100%;
margin-bottom: 0.5em; margin-bottom: 0.5em;
a {
display: inline-block;
word-break: break-all;
}
small { small {
font-weight: lighter; font-weight: lighter;
} }
@ -568,6 +494,11 @@ $status-margin: 0.75em;
justify-content: space-between; justify-content: space-between;
line-height: 18px; line-height: 18px;
a {
display: inline-block;
word-break: break-all;
}
.name-and-account-name { .name-and-account-name {
display: flex; display: flex;
min-width: 0; min-width: 0;
@ -600,6 +531,7 @@ $status-margin: 0.75em;
} }
.heading-reply-row { .heading-reply-row {
position: relative;
align-content: baseline; align-content: baseline;
font-size: 12px; font-size: 12px;
line-height: 18px; line-height: 18px;
@ -608,11 +540,13 @@ $status-margin: 0.75em;
flex-wrap: wrap; flex-wrap: wrap;
align-items: stretch; align-items: stretch;
a { > .reply-to-and-accountname > a {
max-width: 100%; max-width: 100%;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
display: inline-block;
word-break: break-all;
} }
} }
@ -639,6 +573,8 @@ $status-margin: 0.75em;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
margin: 0 0.4em 0 0.2em; margin: 0 0.4em 0 0.2em;
color: $fallback--faint;
color: var(--faint, $fallback--faint);
} }
.replies-separator { .replies-separator {

View file

@ -0,0 +1,34 @@
import { find } from 'lodash'
const StatusPopover = {
name: 'StatusPopover',
props: [
'statusId'
],
data () {
return {
popperOptions: {
modifiers: {
preventOverflow: { padding: { top: 50 }, boundariesElement: 'viewport' }
}
}
}
},
computed: {
status () {
return find(this.$store.state.statuses.allStatuses, { id: this.statusId })
}
},
components: {
Status: () => import('../status/status.vue')
},
methods: {
enter () {
if (!this.status) {
this.$store.dispatch('fetchStatus', this.statusId)
}
}
}
}
export default StatusPopover

View file

@ -0,0 +1,85 @@
<template>
<v-popover
popover-class="status-popover"
placement="top-start"
:popper-options="popperOptions"
@show="enter()"
>
<template slot="popover">
<Status
v-if="status"
:is-preview="true"
:statusoid="status"
:compact="true"
/>
<div
v-else
class="status-preview-loading"
>
<i class="icon-spin4 animate-spin" />
</div>
</template>
<slot />
</v-popover>
</template>
<script src="./status_popover.js" ></script>
<style lang="scss">
@import '../../_variables.scss';
.tooltip.popover.status-popover {
font-size: 1rem;
min-width: 15em;
max-width: 95%;
margin-left: 0.5em;
.popover-inner {
border-color: $fallback--border;
border-color: var(--border, $fallback--border);
border-style: solid;
border-width: 1px;
border-radius: $fallback--tooltipRadius;
border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5);
box-shadow: var(--popupShadow);
}
.popover-arrow::before {
position: absolute;
content: '';
left: -7px;
border: solid 7px transparent;
z-index: -1;
}
&[x-placement^="bottom-start"] .popover-arrow::before {
top: -2px;
border-top-width: 0;
border-bottom-color: $fallback--border;
border-bottom-color: var(--border, $fallback--border);
}
&[x-placement^="top-start"] .popover-arrow::before {
bottom: -2px;
border-bottom-width: 0;
border-top-color: $fallback--border;
border-top-color: var(--border, $fallback--border);
}
.status-el.status-el {
border: none;
}
.status-preview-loading {
padding: 1em;
text-align: center;
i {
font-size: 2em;
}
}
}
</style>

View file

@ -45,8 +45,8 @@
"error": "Se ha producido un error al importar el archivo." "error": "Se ha producido un error al importar el archivo."
}, },
"login": { "login": {
"login": "Identificación", "login": "Identificarse",
"description": "Identificación con OAuth", "description": "Identificarse con OAuth",
"logout": "Cerrar sesión", "logout": "Cerrar sesión",
"password": "Contraseña", "password": "Contraseña",
"placeholder": "p.ej. lain", "placeholder": "p.ej. lain",

View file

@ -68,6 +68,7 @@
}, },
"nav": { "nav": {
"about": "Honi buruz", "about": "Honi buruz",
"administration": "Administrazioa",
"back": "Atzera", "back": "Atzera",
"chat": "Txat lokala", "chat": "Txat lokala",
"friend_requests": "Jarraitzeko eskaerak", "friend_requests": "Jarraitzeko eskaerak",
@ -106,6 +107,15 @@
"expired": "Inkesta {0} bukatu zen", "expired": "Inkesta {0} bukatu zen",
"not_enough_options": "Aukera gutxiegi inkestan" "not_enough_options": "Aukera gutxiegi inkestan"
}, },
"emoji": {
"stickers": "Pegatinak",
"emoji": "Emoji",
"keep_open": "Mantendu hautatzailea zabalik",
"search_emoji": "Bilatu emoji bat",
"add_emoji": "Emoji bat gehitu",
"custom": "Ohiko emojiak",
"unicode": "Unicode emojiak"
},
"stickers": { "stickers": {
"add_sticker": "Pegatina gehitu" "add_sticker": "Pegatina gehitu"
}, },
@ -199,12 +209,12 @@
"avatarRadius": "Avatarrak", "avatarRadius": "Avatarrak",
"background": "Atzeko planoa", "background": "Atzeko planoa",
"bio": "Biografia", "bio": "Biografia",
"block_export": "Bloke esportatzea", "block_export": "Blokeatu dituzunak esportatu",
"block_export_button": "Esportatu zure blokeak csv fitxategi batera", "block_export_button": "Esportatu blokeatutakoak csv fitxategi batera",
"block_import": "Bloke inportazioa", "block_import": "Blokeatu dituzunak inportatu",
"block_import_error": "Errorea blokeak inportatzen", "block_import_error": "Errorea blokeatutakoak inportatzen",
"blocks_imported": "Blokeak inportaturik! Hauek prozesatzeak denbora hartuko du.", "blocks_imported": "Blokeatutakoak inportaturik! Hauek prozesatzeak denbora hartuko du.",
"blocks_tab": "Blokeak", "blocks_tab": "Blokeatutakoak",
"btnRadius": "Botoiak", "btnRadius": "Botoiak",
"cBlue": "Urdina (erantzun, jarraitu)", "cBlue": "Urdina (erantzun, jarraitu)",
"cGreen": "Berdea (Bertxiotu)", "cGreen": "Berdea (Bertxiotu)",
@ -222,7 +232,9 @@
"data_import_export_tab": "Datuak Inportatu / Esportatu", "data_import_export_tab": "Datuak Inportatu / Esportatu",
"default_vis": "Lehenetsitako ikusgaitasunak", "default_vis": "Lehenetsitako ikusgaitasunak",
"delete_account": "Ezabatu kontua", "delete_account": "Ezabatu kontua",
"discoverable": "Baimendu zure kontua kanpo bilaketa-emaitzetan eta bestelako zerbitzuetan agertzea",
"delete_account_description": "Betirako ezabatu zure kontua eta zure mezu guztiak", "delete_account_description": "Betirako ezabatu zure kontua eta zure mezu guztiak",
"pad_emoji": "Zuriuneak gehitu emoji bat aukeratzen denean",
"delete_account_error": "Arazo bat gertatu da zure kontua ezabatzerakoan. Arazoa jarraitu eskero, administratzailearekin harremanetan jarri.", "delete_account_error": "Arazo bat gertatu da zure kontua ezabatzerakoan. Arazoa jarraitu eskero, administratzailearekin harremanetan jarri.",
"delete_account_instructions": "Idatzi zure pasahitza kontua ezabatzeko.", "delete_account_instructions": "Idatzi zure pasahitza kontua ezabatzeko.",
"avatar_size_instruction": "Avatar irudien gomendatutako gutxieneko tamaina 150x150 pixel dira.", "avatar_size_instruction": "Avatar irudien gomendatutako gutxieneko tamaina 150x150 pixel dira.",
@ -254,7 +266,7 @@
"instance_default": "(lehenetsia: {value})", "instance_default": "(lehenetsia: {value})",
"instance_default_simple": "(lehenetsia)", "instance_default_simple": "(lehenetsia)",
"interface": "Interfazea", "interface": "Interfazea",
"interfaceLanguage": "Interfaze hizkuntza", "interfaceLanguage": "Interfazearen hizkuntza",
"invalid_theme_imported": "Hautatutako fitxategia ez da onartutako Pleroma gaia. Ez da zure gaian aldaketarik burutu.", "invalid_theme_imported": "Hautatutako fitxategia ez da onartutako Pleroma gaia. Ez da zure gaian aldaketarik burutu.",
"limited_availability": "Ez dago erabilgarri zure nabigatzailean", "limited_availability": "Ez dago erabilgarri zure nabigatzailean",
"links": "Estekak", "links": "Estekak",
@ -277,6 +289,8 @@
"no_mutes": "Ez daude erabiltzaile mututuak", "no_mutes": "Ez daude erabiltzaile mututuak",
"hide_follows_description": "Ez erakutsi nor jarraitzen ari naizen", "hide_follows_description": "Ez erakutsi nor jarraitzen ari naizen",
"hide_followers_description": "Ez erakutsi nor ari den ni jarraitzen", "hide_followers_description": "Ez erakutsi nor ari den ni jarraitzen",
"hide_follows_count_description": "Ez erakutsi jarraitzen ari naizen kontuen kopurua",
"hide_followers_count_description": "Ez erakutsi nire jarraitzaileen kontuen kopurua",
"show_admin_badge": "Erakutsi Administratzaile etiketa nire profilan", "show_admin_badge": "Erakutsi Administratzaile etiketa nire profilan",
"show_moderator_badge": "Erakutsi Moderatzaile etiketa nire profilan", "show_moderator_badge": "Erakutsi Moderatzaile etiketa nire profilan",
"nsfw_clickthrough": "Gaitu klika hunkigarri eranskinak ezkutatzeko", "nsfw_clickthrough": "Gaitu klika hunkigarri eranskinak ezkutatzeko",
@ -449,7 +463,7 @@
}, },
"version": { "version": {
"title": "Bertsioa", "title": "Bertsioa",
"backend_version": "Backend Bertsio", "backend_version": "Backend Bertsioa",
"frontend_version": "Frontend Bertsioa" "frontend_version": "Frontend Bertsioa"
} }
}, },
@ -529,6 +543,7 @@
"follows_you": "Jarraitzen dizu!", "follows_you": "Jarraitzen dizu!",
"its_you": "Zu zara!", "its_you": "Zu zara!",
"media": "Multimedia", "media": "Multimedia",
"mention": "Aipatu",
"mute": "Isilarazi", "mute": "Isilarazi",
"muted": "Isilduta", "muted": "Isilduta",
"per_day": "eguneko", "per_day": "eguneko",

View file

@ -41,7 +41,13 @@ Vue.use(VueChatScroll)
Vue.use(VueClickOutside) Vue.use(VueClickOutside)
Vue.use(PortalVue) Vue.use(PortalVue)
Vue.use(VBodyScrollLock) Vue.use(VBodyScrollLock)
Vue.use(VTooltip) Vue.use(VTooltip, {
popover: {
defaultTrigger: 'hover click',
defaultContainer: false,
defaultOffset: 5
}
})
const i18n = new VueI18n({ const i18n = new VueI18n({
// By default, use the browser locale, we will update it if neccessary // By default, use the browser locale, we will update it if neccessary

View file

@ -537,6 +537,10 @@ const statuses = {
setNotificationsSilence ({ rootState, commit }, { value }) { setNotificationsSilence ({ rootState, commit }, { value }) {
commit('setNotificationsSilence', { value }) commit('setNotificationsSilence', { value })
}, },
fetchStatus ({ rootState, dispatch }, id) {
rootState.api.backendInteractor.fetchStatus({ id })
.then((status) => dispatch('addNewStatuses', { statuses: [status] }))
},
deleteStatus ({ rootState, commit }, status) { deleteStatus ({ rootState, commit }, status) {
commit('setDeleted', { status }) commit('setDeleted', { status })
apiService.deleteStatus({ id: status.id, credentials: rootState.users.currentUser.credentials }) apiService.deleteStatus({ id: status.id, credentials: rootState.users.currentUser.credentials })