Compare commits

...

8 commits

Author SHA1 Message Date
Henry Jameson
3cc84a867d Merge remote-tracking branch 'origin/develop' into experiment-still-emoji
* origin/develop:
  fix warnings
  oops
  backport vue3 changes related to emoji-input
  migrate to v-slot
2021-05-31 16:50:08 +03:00
Henry Jameson
8e1182651a experimental still emoji 2021-05-31 16:49:28 +03:00
HJ
7bd18cda64 Merge branch 'vue3compat-emoji-input' into 'develop'
Refactor EmojiInput for better vue3 compatibility

See merge request pleroma/pleroma-fe!1382
2021-05-31 11:17:42 +00:00
HJ
0ca0e642a4 Merge branch 'v-slot-upgrade' into 'develop'
Change old slot syntax (removed in vue3) to new one

See merge request pleroma/pleroma-fe!1379
2021-05-31 11:15:44 +00:00
Henry Jameson
80220c1b07 fix warnings 2021-05-31 14:08:12 +03:00
Henry Jameson
159bbed2f9 oops 2021-05-31 13:59:44 +03:00
Henry Jameson
40ac9ef499 backport vue3 changes related to emoji-input 2021-04-18 17:03:31 +03:00
Henry Jameson
61dcdbf992 migrate to v-slot 2021-04-07 22:42:34 +03:00
26 changed files with 362 additions and 315 deletions

View file

@ -6,10 +6,7 @@
:bound-to="{ x: 'container' }" :bound-to="{ x: 'container' }"
remove-padding remove-padding
> >
<div <template v-slot:content>
slot="content"
class="account-tools-popover"
>
<div class="dropdown-menu"> <div class="dropdown-menu">
<template v-if="relationship.following"> <template v-if="relationship.following">
<button <button
@ -59,16 +56,15 @@
{{ $t('user_card.message') }} {{ $t('user_card.message') }}
</button> </button>
</div> </div>
</div> </template>
<div <template v-slot:trigger>
slot="trigger" <button class="button-unstyled ellipsis-button">
class="ellipsis-button" <FAIcon
> class="icon"
<FAIcon icon="ellipsis-v"
class="icon" />
icon="ellipsis-v" </button>
/> </template>
</div>
</Popover> </Popover>
</div> </div>
</template> </template>
@ -83,7 +79,6 @@
} }
.ellipsis-button { .ellipsis-button {
cursor: pointer;
width: 2.5em; width: 2.5em;
margin: -0.5em 0; margin: -0.5em 0;
padding: 0.5em 0; padding: 0.5em 0;

View file

@ -23,10 +23,7 @@
class="timeline" class="timeline"
> >
<List :items="sortedChatList"> <List :items="sortedChatList">
<template <template v-slot:item="{item}">
slot="item"
slot-scope="{item}"
>
<ChatListItem <ChatListItem
:key="item.id" :key="item.id"
:compact="false" :compact="false"

View file

@ -50,7 +50,7 @@
@show="menuOpened = true" @show="menuOpened = true"
@close="menuOpened = false" @close="menuOpened = false"
> >
<div slot="content"> <template v-slot:content>
<div class="dropdown-menu"> <div class="dropdown-menu">
<button <button
class="button-default dropdown-item dropdown-item-icon" class="button-default dropdown-item dropdown-item-icon"
@ -59,26 +59,28 @@
<FAIcon icon="times" /> {{ $t("chats.delete") }} <FAIcon icon="times" /> {{ $t("chats.delete") }}
</button> </button>
</div> </div>
</div> </template>
<button <template v-slot:trigger>
slot="trigger" <button
class="button-default menu-icon" class="button-default menu-icon"
:title="$t('chats.more')" :title="$t('chats.more')"
> >
<FAIcon icon="ellipsis-h" /> <FAIcon icon="ellipsis-h" />
</button> </button>
</template>
</Popover> </Popover>
</div> </div>
<StatusContent <StatusContent
:status="messageForStatusContent" :status="messageForStatusContent"
:full-content="true" :full-content="true"
> >
<span <template v-slot:footer>
slot="footer" <span
class="created-at" class="created-at"
> >
{{ createdAt }} {{ createdAt }}
</span> </span>
</template>
</StatusContent> </StatusContent>
</div> </div>
</div> </div>

View file

@ -9,7 +9,7 @@
class="btn button-default" class="btn button-default"
> >
{{ $t('domain_mute_card.unmute') }} {{ $t('domain_mute_card.unmute') }}
<template slot="progress"> <template v-slot:progress>
{{ $t('domain_mute_card.unmute_progress') }} {{ $t('domain_mute_card.unmute_progress') }}
</template> </template>
</ProgressButton> </ProgressButton>
@ -19,7 +19,7 @@
class="btn button-default" class="btn button-default"
> >
{{ $t('domain_mute_card.mute') }} {{ $t('domain_mute_card.mute') }}
<template slot="progress"> <template v-slot:progress>
{{ $t('domain_mute_card.mute_progress') }} {{ $t('domain_mute_card.mute_progress') }}
</template> </template>
</ProgressButton> </ProgressButton>

View file

@ -57,6 +57,7 @@ const EmojiInput = {
required: true, required: true,
type: Function type: Function
}, },
// TODO VUE3: change to modelValue, change 'input' event to 'input'
value: { value: {
/** /**
* Used for v-model * Used for v-model
@ -143,32 +144,31 @@ const EmojiInput = {
} }
}, },
mounted () { mounted () {
const slots = this.$slots.default const { root } = this.$refs
if (!slots || slots.length === 0) return const input = root.querySelector('.emoji-input > input') || root.querySelector('.emoji-input > textarea')
const input = slots.find(slot => ['input', 'textarea'].includes(slot.tag))
if (!input) return if (!input) return
this.input = input this.input = input
this.resize() this.resize()
input.elm.addEventListener('blur', this.onBlur) input.addEventListener('blur', this.onBlur)
input.elm.addEventListener('focus', this.onFocus) input.addEventListener('focus', this.onFocus)
input.elm.addEventListener('paste', this.onPaste) input.addEventListener('paste', this.onPaste)
input.elm.addEventListener('keyup', this.onKeyUp) input.addEventListener('keyup', this.onKeyUp)
input.elm.addEventListener('keydown', this.onKeyDown) input.addEventListener('keydown', this.onKeyDown)
input.elm.addEventListener('click', this.onClickInput) input.addEventListener('click', this.onClickInput)
input.elm.addEventListener('transitionend', this.onTransition) input.addEventListener('transitionend', this.onTransition)
input.elm.addEventListener('input', this.onInput) input.addEventListener('input', this.onInput)
}, },
unmounted () { unmounted () {
const { input } = this const { input } = this
if (input) { if (input) {
input.elm.removeEventListener('blur', this.onBlur) input.removeEventListener('blur', this.onBlur)
input.elm.removeEventListener('focus', this.onFocus) input.removeEventListener('focus', this.onFocus)
input.elm.removeEventListener('paste', this.onPaste) input.removeEventListener('paste', this.onPaste)
input.elm.removeEventListener('keyup', this.onKeyUp) input.removeEventListener('keyup', this.onKeyUp)
input.elm.removeEventListener('keydown', this.onKeyDown) input.removeEventListener('keydown', this.onKeyDown)
input.elm.removeEventListener('click', this.onClickInput) input.removeEventListener('click', this.onClickInput)
input.elm.removeEventListener('transitionend', this.onTransition) input.removeEventListener('transitionend', this.onTransition)
input.elm.removeEventListener('input', this.onInput) input.removeEventListener('input', this.onInput)
} }
}, },
watch: { watch: {
@ -216,7 +216,7 @@ const EmojiInput = {
}, 0) }, 0)
}, },
togglePicker () { togglePicker () {
this.input.elm.focus() this.input.focus()
this.showPicker = !this.showPicker this.showPicker = !this.showPicker
if (this.showPicker) { if (this.showPicker) {
this.scrollIntoView() this.scrollIntoView()
@ -262,13 +262,13 @@ const EmojiInput = {
this.$emit('input', newValue) this.$emit('input', newValue)
const position = this.caret + (insertion + spaceAfter + spaceBefore).length const position = this.caret + (insertion + spaceAfter + spaceBefore).length
if (!keepOpen) { if (!keepOpen) {
this.input.elm.focus() this.input.focus()
} }
this.$nextTick(function () { this.$nextTick(function () {
// Re-focus inputbox after clicking suggestion // Re-focus inputbox after clicking suggestion
// Set selection right after the replacement instead of the very end // Set selection right after the replacement instead of the very end
this.input.elm.setSelectionRange(position, position) this.input.setSelectionRange(position, position)
this.caret = position this.caret = position
}) })
}, },
@ -285,9 +285,9 @@ const EmojiInput = {
this.$nextTick(function () { this.$nextTick(function () {
// Re-focus inputbox after clicking suggestion // Re-focus inputbox after clicking suggestion
this.input.elm.focus() this.input.focus()
// Set selection right after the replacement instead of the very end // Set selection right after the replacement instead of the very end
this.input.elm.setSelectionRange(position, position) this.input.setSelectionRange(position, position)
this.caret = position this.caret = position
}) })
e.preventDefault() e.preventDefault()
@ -349,7 +349,7 @@ const EmojiInput = {
} }
this.$nextTick(() => { this.$nextTick(() => {
const { offsetHeight } = this.input.elm const { offsetHeight } = this.input
const { picker } = this.$refs const { picker } = this.$refs
const pickerBottom = picker.$el.getBoundingClientRect().bottom const pickerBottom = picker.$el.getBoundingClientRect().bottom
if (pickerBottom > window.innerHeight) { if (pickerBottom > window.innerHeight) {
@ -414,8 +414,8 @@ const EmojiInput = {
// Scroll the input element to the position of the cursor // Scroll the input element to the position of the cursor
this.$nextTick(() => { this.$nextTick(() => {
this.input.elm.blur() this.input.blur()
this.input.elm.focus() this.input.focus()
}) })
} }
// Disable suggestions hotkeys if suggestions are hidden // Disable suggestions hotkeys if suggestions are hidden
@ -444,7 +444,7 @@ const EmojiInput = {
// de-focuses the element (i.e. default browser behavior) // de-focuses the element (i.e. default browser behavior)
if (key === 'Escape') { if (key === 'Escape') {
if (!this.temporarilyHideSuggestions) { if (!this.temporarilyHideSuggestions) {
this.input.elm.focus() this.input.focus()
} }
} }
@ -480,7 +480,7 @@ const EmojiInput = {
if (!panel) return if (!panel) return
const picker = this.$refs.picker.$el const picker = this.$refs.picker.$el
const panelBody = this.$refs['panel-body'] const panelBody = this.$refs['panel-body']
const { offsetHeight, offsetTop } = this.input.elm const { offsetHeight, offsetTop } = this.input
const offsetBottom = offsetTop + offsetHeight const offsetBottom = offsetTop + offsetHeight
this.setPlacement(panelBody, panel, offsetBottom) this.setPlacement(panelBody, panel, offsetBottom)
@ -494,7 +494,7 @@ const EmojiInput = {
if (this.placement === 'top' || (this.placement === 'auto' && this.overflowsBottom(container))) { if (this.placement === 'top' || (this.placement === 'auto' && this.overflowsBottom(container))) {
target.style.top = 'auto' target.style.top = 'auto'
target.style.bottom = this.input.elm.offsetHeight + 'px' target.style.bottom = this.input.offsetHeight + 'px'
} }
}, },
overflowsBottom (el) { overflowsBottom (el) {

View file

@ -3,6 +3,7 @@
v-click-outside="onClickOutside" v-click-outside="onClickOutside"
class="emoji-input" class="emoji-input"
:class="{ 'with-picker': !hideEmojiButton }" :class="{ 'with-picker': !hideEmojiButton }"
ref='root'
> >
<slot /> <slot />
<template v-if="enableEmojiPicker"> <template v-if="enableEmojiPicker">

View file

@ -7,10 +7,7 @@
:bound-to="{ x: 'container' }" :bound-to="{ x: 'container' }"
remove-padding remove-padding
> >
<div <template v-slot:content="{close}">
slot="content"
slot-scope="{close}"
>
<div class="dropdown-menu"> <div class="dropdown-menu">
<button <button
v-if="canMute && !status.thread_muted" v-if="canMute && !status.thread_muted"
@ -120,16 +117,15 @@
/><span>{{ $t("user_card.report") }}</span> /><span>{{ $t("user_card.report") }}</span>
</button> </button>
</div> </div>
</div> </template>
<span <template v-slot:trigger>
slot="trigger" <button class="button-unstyled popover-trigger">
class="popover-trigger" <FAIcon
> class="fa-scale-110 fa-old-padding"
<FAIcon icon="ellipsis-h"
class="fa-scale-110 fa-old-padding" />
icon="ellipsis-h" </button>
/> </template>
</span>
</Popover> </Popover>
</template> </template>

View file

@ -8,7 +8,7 @@
@show="setToggled(true)" @show="setToggled(true)"
@close="setToggled(false)" @close="setToggled(false)"
> >
<div slot="content"> <template v-slot:content>
<div class="dropdown-menu"> <div class="dropdown-menu">
<span v-if="user.is_local"> <span v-if="user.is_local">
<button <button
@ -121,26 +121,27 @@
</button> </button>
</span> </span>
</div> </div>
</div> </template>
<button <template v-slot:trigger>
slot="trigger" <button
class="btn button-default btn-block moderation-tools-button" class="btn button-default btn-block moderation-tools-button"
:class="{ toggled }" :class="{ toggled }"
> >
{{ $t('user_card.admin_menu.moderation') }} {{ $t('user_card.admin_menu.moderation') }}
<FAIcon icon="chevron-down" /> <FAIcon icon="chevron-down" />
</button> </button>
</template>
</Popover> </Popover>
<portal to="modal"> <portal to="modal">
<DialogModal <DialogModal
v-if="showDeleteUserDialog" v-if="showDeleteUserDialog"
:on-cancel="deleteUserDialog.bind(this, false)" :on-cancel="deleteUserDialog.bind(this, false)"
> >
<template slot="header"> <template v-slot:header>
{{ $t('user_card.admin_menu.delete_user') }} {{ $t('user_card.admin_menu.delete_user') }}
</template> </template>
<p>{{ $t('user_card.admin_menu.delete_user_confirmation') }}</p> <p>{{ $t('user_card.admin_menu.delete_user_confirmation') }}</p>
<template slot="footer"> <template v-slot:footer>
<button <button
class="btn button-default" class="btn button-default"
@click="deleteUserDialog(false)" @click="deleteUserDialog(false)"

View file

@ -2,6 +2,12 @@
// TODO Copypaste from Status, should unify it somehow // TODO Copypaste from Status, should unify it somehow
.Notification { .Notification {
&:hover {
--_still-image-img-visibility: visible;
--_still-image-canvas-visibility: hidden;
--_still-image-label-visibility: hidden;
}
&.-muted { &.-muted {
padding: 0.25em 0.6em; padding: 0.25em 0.6em;
height: 1.2em; height: 1.2em;

View file

@ -5,9 +5,7 @@
placement="bottom" placement="bottom"
:bound-to="{ x: 'container' }" :bound-to="{ x: 'container' }"
> >
<template <template v-slot:content>
v-slot:content
>
<div class="dropdown-menu"> <div class="dropdown-menu">
<button <button
class="button-default dropdown-item" class="button-default dropdown-item"
@ -66,7 +64,9 @@
</div> </div>
</template> </template>
<template v-slot:trigger> <template v-slot:trigger>
<FAIcon icon="filter" /> <button class="button-unstyled">
<FAIcon icon="filter" />
</button>
</template> </template>
</Popover> </Popover>
</template> </template>

View file

@ -54,7 +54,7 @@ const Popover = {
} }
// Popover will be anchored around this element, trigger ref is the container, so // Popover will be anchored around this element, trigger ref is the container, so
// its children are what are inside the slot. Expect only one slot="trigger". // its children are what are inside the slot. Expect only one v-slot:trigger.
const anchorEl = (this.$refs.trigger && this.$refs.trigger.children[0]) || this.$el const anchorEl = (this.$refs.trigger && this.$refs.trigger.children[0]) || this.$el
// SVGs don't have offsetWidth/Height, use fallback // SVGs don't have offsetWidth/Height, use fallback
const anchorWidth = anchorEl.offsetWidth || anchorEl.clientWidth const anchorWidth = anchorEl.offsetWidth || anchorEl.clientWidth

View file

@ -8,10 +8,7 @@
remove-padding remove-padding
@show="focusInput" @show="focusInput"
> >
<div <template v-slot:content="{close}">
slot="content"
slot-scope="{close}"
>
<div class="reaction-picker-filter"> <div class="reaction-picker-filter">
<input <input
v-model="filterWord" v-model="filterWord"
@ -41,17 +38,18 @@
</span> </span>
<div class="reaction-bottom-fader" /> <div class="reaction-bottom-fader" />
</div> </div>
</div> </template>
<span <template v-slot:trigger>
slot="trigger" <button
class="popover-trigger" class="button-unstyled popover-trigger"
:title="$t('tool_tip.add_reaction')" :title="$t('tool_tip.add_reaction')"
> >
<FAIcon <FAIcon
class="fa-scale-110 fa-old-padding" class="fa-scale-110 fa-old-padding"
:icon="['far', 'smile-beam']" :icon="['far', 'smile-beam']"
/> />
</span> </button>
</template>
</Popover> </Popover>
</template> </template>

View file

@ -24,10 +24,7 @@
:items="items" :items="items"
:get-key="getKey" :get-key="getKey"
> >
<template <template v-slot:item="{item}">
slot="item"
slot-scope="{item}"
>
<div <div
class="selectable-list-item-inner" class="selectable-list-item-inner"
:class="{ 'selectable-list-item-selected-inner': isSelected(item) }" :class="{ 'selectable-list-item-selected-inner': isSelected(item) }"
@ -44,7 +41,7 @@
/> />
</div> </div>
</template> </template>
<template slot="empty"> <template v-slot:empty>
<slot name="empty" /> <slot name="empty" />
</template> </template>
</List> </List>

View file

@ -6,18 +6,18 @@
<Popover <Popover
trigger="hover" trigger="hover"
> >
<span slot="trigger"> <template v-slot:trigger>
&nbsp; &nbsp;
<FAIcon <FAIcon
icon="wrench" icon="wrench"
:aria-label="$t('settings.setting_changed')"
/> />
</span> </template>
<div <template v-slot:content>
slot="content" <div class="modified-tooltip">
class="modified-tooltip" {{ $t('settings.setting_changed') }}
> </div>
{{ $t('settings.setting_changed') }} </template>
</div>
</Popover> </Popover>
</span> </span>
</template> </template>

View file

@ -62,20 +62,18 @@
:bound-to="{ x: 'container' }" :bound-to="{ x: 'container' }"
remove-padding remove-padding
> >
<button <template v-slot:trigger>
slot="trigger" <button
class="btn button-default" class="btn button-default"
:title="$t('general.close')" :title="$t('general.close')"
> >
<span>{{ $t("settings.file_export_import.backup_restore") }}</span> <span>{{ $t("settings.file_export_import.backup_restore") }}</span>
<FAIcon <FAIcon
icon="chevron-down" icon="chevron-down"
/> />
</button> </button>
<div </template>
slot="content" <template v-slot:content="{close}">
slot-scope="{close}"
>
<div class="dropdown-menu"> <div class="dropdown-menu">
<button <button
class="button-default dropdown-item dropdown-item-icon" class="button-default dropdown-item dropdown-item-icon"
@ -108,7 +106,7 @@
/><span>{{ $t("settings.file_export_import.restore_settings") }}</span> /><span>{{ $t("settings.file_export_import.restore_settings") }}</span>
</button> </button>
</div> </div>
</div> </template>
</Popover> </Popover>
</div> </div>
</div> </div>

View file

@ -10,20 +10,18 @@
:query="queryUserIds" :query="queryUserIds"
:placeholder="$t('settings.search_user_to_block')" :placeholder="$t('settings.search_user_to_block')"
> >
<BlockCard <template v-slot="row">
slot-scope="row" <BlockCard
:user-id="row.item" :user-id="row.item"
/> />
</template>
</Autosuggest> </Autosuggest>
</div> </div>
<BlockList <BlockList
:refresh="true" :refresh="true"
:get-key="i => i" :get-key="i => i"
> >
<template <template v-slot:header="{selected}">
slot="header"
slot-scope="{selected}"
>
<div class="bulk-actions"> <div class="bulk-actions">
<ProgressButton <ProgressButton
v-if="selected.length > 0" v-if="selected.length > 0"
@ -31,7 +29,7 @@
:click="() => blockUsers(selected)" :click="() => blockUsers(selected)"
> >
{{ $t('user_card.block') }} {{ $t('user_card.block') }}
<template slot="progress"> <template v-slot:progress>
{{ $t('user_card.block_progress') }} {{ $t('user_card.block_progress') }}
</template> </template>
</ProgressButton> </ProgressButton>
@ -41,19 +39,16 @@
:click="() => unblockUsers(selected)" :click="() => unblockUsers(selected)"
> >
{{ $t('user_card.unblock') }} {{ $t('user_card.unblock') }}
<template slot="progress"> <template v-slot:progress>
{{ $t('user_card.unblock_progress') }} {{ $t('user_card.unblock_progress') }}
</template> </template>
</ProgressButton> </ProgressButton>
</div> </div>
</template> </template>
<template <template v-slot:item="{item}">
slot="item"
slot-scope="{item}"
>
<BlockCard :user-id="item" /> <BlockCard :user-id="item" />
</template> </template>
<template slot="empty"> <template v-slot:empty>
{{ $t('settings.no_blocks') }} {{ $t('settings.no_blocks') }}
</template> </template>
</BlockList> </BlockList>
@ -68,20 +63,18 @@
:query="queryUserIds" :query="queryUserIds"
:placeholder="$t('settings.search_user_to_mute')" :placeholder="$t('settings.search_user_to_mute')"
> >
<MuteCard <template v-slot="row">
slot-scope="row" <MuteCard
:user-id="row.item" :user-id="row.item"
/> />
</template>
</Autosuggest> </Autosuggest>
</div> </div>
<MuteList <MuteList
:refresh="true" :refresh="true"
:get-key="i => i" :get-key="i => i"
> >
<template <template v-slot:header="{selected}">
slot="header"
slot-scope="{selected}"
>
<div class="bulk-actions"> <div class="bulk-actions">
<ProgressButton <ProgressButton
v-if="selected.length > 0" v-if="selected.length > 0"
@ -89,7 +82,7 @@
:click="() => muteUsers(selected)" :click="() => muteUsers(selected)"
> >
{{ $t('user_card.mute') }} {{ $t('user_card.mute') }}
<template slot="progress"> <template v-slot:progress>
{{ $t('user_card.mute_progress') }} {{ $t('user_card.mute_progress') }}
</template> </template>
</ProgressButton> </ProgressButton>
@ -99,19 +92,16 @@
:click="() => unmuteUsers(selected)" :click="() => unmuteUsers(selected)"
> >
{{ $t('user_card.unmute') }} {{ $t('user_card.unmute') }}
<template slot="progress"> <template v-slot:progress>
{{ $t('user_card.unmute_progress') }} {{ $t('user_card.unmute_progress') }}
</template> </template>
</ProgressButton> </ProgressButton>
</div> </div>
</template> </template>
<template <template v-slot:item="{item}">
slot="item"
slot-scope="{item}"
>
<MuteCard :user-id="item" /> <MuteCard :user-id="item" />
</template> </template>
<template slot="empty"> <template v-slot:empty>
{{ $t('settings.no_mutes') }} {{ $t('settings.no_mutes') }}
</template> </template>
</MuteList> </MuteList>
@ -124,20 +114,18 @@
:query="queryKnownDomains" :query="queryKnownDomains"
:placeholder="$t('settings.type_domains_to_mute')" :placeholder="$t('settings.type_domains_to_mute')"
> >
<DomainMuteCard <template v-slot="row">
slot-scope="row" <DomainMuteCard
:domain="row.item" :domain="row.item"
/> />
</template>
</Autosuggest> </Autosuggest>
</div> </div>
<DomainMuteList <DomainMuteList
:refresh="true" :refresh="true"
:get-key="i => i" :get-key="i => i"
> >
<template <template v-slot:header="{selected}">
slot="header"
slot-scope="{selected}"
>
<div class="bulk-actions"> <div class="bulk-actions">
<ProgressButton <ProgressButton
v-if="selected.length > 0" v-if="selected.length > 0"
@ -145,19 +133,16 @@
:click="() => unmuteDomains(selected)" :click="() => unmuteDomains(selected)"
> >
{{ $t('domain_mute_card.unmute') }} {{ $t('domain_mute_card.unmute') }}
<template slot="progress"> <template v-slot:progress>
{{ $t('domain_mute_card.unmute_progress') }} {{ $t('domain_mute_card.unmute_progress') }}
</template> </template>
</ProgressButton> </ProgressButton>
</div> </div>
</template> </template>
<template <template v-slot:item="{item}">
slot="item"
slot-scope="{item}"
>
<DomainMuteCard :domain="item" /> <DomainMuteCard :domain="item" />
</template> </template>
<template slot="empty"> <template v-slot:empty>
{{ $t('settings.no_mutes') }} {{ $t('settings.no_mutes') }}
</template> </template>
</DomainMuteList> </DomainMuteList>

View file

@ -1,4 +1,6 @@
import Vue from 'vue'
import Attachment from '../attachment/attachment.vue' import Attachment from '../attachment/attachment.vue'
import StillImage from '../still-image/still-image.vue'
import Poll from '../poll/poll.vue' import Poll from '../poll/poll.vue'
import Gallery from '../gallery/gallery.vue' import Gallery from '../gallery/gallery.vue'
import LinkPreview from '../link-preview/link-preview.vue' import LinkPreview from '../link-preview/link-preview.vue'
@ -40,10 +42,24 @@ const StatusContent = {
showingTall: this.fullContent || (this.inConversation && this.focused), showingTall: this.fullContent || (this.inConversation && this.focused),
showingLongSubject: false, showingLongSubject: false,
// not as computed because it sets the initial state which will be changed later // not as computed because it sets the initial state which will be changed later
expandingSubject: !this.$store.getters.mergedConfig.collapseMessageWithSubject expandingSubject: !this.$store.getters.mergedConfig.collapseMessageWithSubject,
stillImages: []
} }
}, },
updated () {
this.patchEmoji()
},
mounted () {
this.patchEmoji()
},
beforeDestroy() {
this.stillImages.forEach(vm => vm.$destroy())
},
computed: { computed: {
stopGifs () {
return this.$store.getters.mergedConfig.stopGifs
},
localCollapseSubjectDefault () { localCollapseSubjectDefault () {
return this.mergedConfig.collapseMessageWithSubject return this.mergedConfig.collapseMessageWithSubject
}, },
@ -167,6 +183,43 @@ const StatusContent = {
LinkPreview LinkPreview
}, },
methods: { methods: {
patchEmoji () {
[this.$refs.subject, this.$refs.content].forEach(el => {
if (!el) return
const imgs = el.querySelectorAll('*:not(.still-image) > img')
imgs.forEach((img, i) => {
// extract relevant info from image
// TODO do a better job at "cloning" node and splicing it into a span
const { src, title, className } = img
// Outer placeholder, thing that will contain stillimage
const placeholder = document.createElement('span')
// Inner placeholder, this will be REPLACED by stillimage
const placeholderInner = document.createElement('span')
// Use special classname for emoji, just to be safe
placeholder.className = className === 'emoji' ? '__pleromafe_emoji' : 'emoji'
// Overall it seems to be easier to use a wrapper and add stuff to it
// than to add stuff to component (how??)
placeholder.title = title
// Insert inner into outer
placeholder.appendChild(placeholderInner)
// put our placeholder before the image
img.parentNode.insertBefore(placeholder, img)
// Render StillImage into placeholder
const opts = {
el: placeholder,
props: { src }
}
const vm = new Vue({ el: placeholderInner, render: h => h(StillImage, opts)})
this.stillImages.push(vm)
if (img.className === 'emoji') {
img.className = img.className + ' __pleromafe_emoji_orig'
}
})
})
},
linkClicked (event) { linkClicked (event) {
const target = event.target.closest('.status-content a') const target = event.target.closest('.status-content a')
if (target) { if (target) {

View file

@ -1,6 +1,6 @@
<template> <template>
<!-- eslint-disable vue/no-v-html --> <!-- eslint-disable vue/no-v-html -->
<div class="StatusContent"> <div class="StatusContent" :class="{'-stop-gifs': stopGifs}">
<slot name="header" /> <slot name="header" />
<div <div
v-if="status.summary_html" v-if="status.summary_html"
@ -10,6 +10,7 @@
<div <div
class="media-body summary" class="media-body summary"
@click.prevent="linkClicked" @click.prevent="linkClicked"
ref="subject"
v-html="status.summary_html" v-html="status.summary_html"
/> />
<button <button
@ -45,6 +46,7 @@
:class="{ 'single-line': singleLine }" :class="{ 'single-line': singleLine }"
class="status-content media-body" class="status-content media-body"
@click.prevent="linkClicked" @click.prevent="linkClicked"
ref="content"
v-html="postBodyHtml" v-html="postBodyHtml"
/> />
<button <button
@ -140,6 +142,22 @@ $status-margin: 0.75em;
flex: 1; flex: 1;
min-width: 0; min-width: 0;
&:hover {
--_still-image-img-visibility: visible;
--_still-image-canvas-visibility: hidden;
--_still-image-label-visibility: hidden;
}
&.-stop-gifs {
.__pleromafe_emoji {
display: inline-block;
}
.__pleromafe_emoji_orig {
display: none;
}
}
.status-content-wrapper { .status-content-wrapper {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -192,11 +210,21 @@ $status-margin: 0.75em;
object-fit: contain; object-fit: contain;
&.emoji { &.emoji {
width: 32px; width: var(--big-emoji-size, 32px);
height: 32px; height: var(--big-emoji-size, 32px);
} }
} }
.__pleromafe_emoji {
display: none; // overriden elsewhere (in the beginning of css file)
max-width: 100%;
max-height: 400px;
vertical-align: middle;
object-fit: contain;
width: var(--big-emoji-size, 32px);
height: var(--big-emoji-size, 32px);
}
.summary-wrapper { .summary-wrapper {
margin-bottom: 0.5em; margin-bottom: 0.5em;
border-style: solid; border-style: solid;

View file

@ -5,12 +5,10 @@
:bound-to="{ x: 'container' }" :bound-to="{ x: 'container' }"
@show="enter" @show="enter"
> >
<template slot="trigger"> <template v-slot:trigger>
<slot /> <slot />
</template> </template>
<div <template v-slot:content>
slot="content"
>
<Status <Status
v-if="status" v-if="status"
:is-preview="true" :is-preview="true"
@ -33,7 +31,7 @@
size="2x" size="2x"
/> />
</div> </div>
</div> </template>
</Popover> </Popover>
</template> </template>

View file

@ -1,3 +1,5 @@
import Vue from 'vue'
const StillImage = { const StillImage = {
props: [ props: [
'src', 'src',
@ -8,8 +10,9 @@ const StillImage = {
'alt' 'alt'
], ],
data () { data () {
console.log('test', this.$store === undefined)
return { return {
stopGifs: this.$store.getters.mergedConfig.stopGifs stopGifs: this.$store === undefined ? true : this.$store.getters.mergedConfig.stopGifs
} }
}, },
computed: { computed: {
@ -36,4 +39,4 @@ const StillImage = {
} }
} }
export default StillImage export default Vue.component('still-image', StillImage)

View file

@ -4,77 +4,78 @@
class="TimelineQuickSettings" class="TimelineQuickSettings"
:bound-to="{ x: 'container' }" :bound-to="{ x: 'container' }"
> >
<div <template v-slot:content>
slot="content" <div class="dropdown-menu">
class="dropdown-menu" <div v-if="loggedIn">
> <button
<div v-if="loggedIn"> class="button-default dropdown-item"
@click="replyVisibilityAll = true"
>
<span
class="menu-checkbox"
:class="{ 'menu-checkbox-radio': replyVisibilityAll }"
/>{{ $t('settings.reply_visibility_all') }}
</button>
<button
class="button-default dropdown-item"
@click="replyVisibilityFollowing = true"
>
<span
class="menu-checkbox"
:class="{ 'menu-checkbox-radio': replyVisibilityFollowing }"
/>{{ $t('settings.reply_visibility_following_short') }}
</button>
<button
class="button-default dropdown-item"
@click="replyVisibilitySelf = true"
>
<span
class="menu-checkbox"
:class="{ 'menu-checkbox-radio': replyVisibilitySelf }"
/>{{ $t('settings.reply_visibility_self_short') }}
</button>
<div
role="separator"
class="dropdown-divider"
/>
</div>
<button <button
class="button-default dropdown-item" class="button-default dropdown-item"
@click="replyVisibilityAll = true" @click="hideMedia = !hideMedia"
> >
<span <span
class="menu-checkbox" class="menu-checkbox"
:class="{ 'menu-checkbox-radio': replyVisibilityAll }" :class="{ 'menu-checkbox-checked': hideMedia }"
/>{{ $t('settings.reply_visibility_all') }} />{{ $t('settings.hide_media_previews') }}
</button> </button>
<button <button
class="button-default dropdown-item" class="button-default dropdown-item"
@click="replyVisibilityFollowing = true" @click="hideMutedPosts = !hideMutedPosts"
> >
<span <span
class="menu-checkbox" class="menu-checkbox"
:class="{ 'menu-checkbox-radio': replyVisibilityFollowing }" :class="{ 'menu-checkbox-checked': hideMutedPosts }"
/>{{ $t('settings.reply_visibility_following_short') }} />{{ $t('settings.hide_all_muted_posts') }}
</button> </button>
<button <button
class="button-default dropdown-item" class="button-default dropdown-item dropdown-item-icon"
@click="replyVisibilitySelf = true" @click="openTab('filtering')"
> >
<span <FAIcon icon="font" />{{ $t('settings.word_filter') }}
class="menu-checkbox" </button>
:class="{ 'menu-checkbox-radio': replyVisibilitySelf }" <button
/>{{ $t('settings.reply_visibility_self_short') }} class="button-default dropdown-item dropdown-item-icon"
@click="openTab('general')"
>
<FAIcon icon="wrench" />{{ $t('settings.more_settings') }}
</button> </button>
<div
role="separator"
class="dropdown-divider"
/>
</div> </div>
<button </template>
class="button-default dropdown-item" <template v-slot:trigger>
@click="hideMedia = !hideMedia" <button class="button-unstyled">
> <FAIcon icon="filter" />
<span
class="menu-checkbox"
:class="{ 'menu-checkbox-checked': hideMedia }"
/>{{ $t('settings.hide_media_previews') }}
</button> </button>
<button </template>
class="button-default dropdown-item"
@click="hideMutedPosts = !hideMutedPosts"
>
<span
class="menu-checkbox"
:class="{ 'menu-checkbox-checked': hideMutedPosts }"
/>{{ $t('settings.hide_all_muted_posts') }}
</button>
<button
class="button-default dropdown-item dropdown-item-icon"
@click="openTab('filtering')"
>
<FAIcon icon="font" />{{ $t('settings.word_filter') }}
</button>
<button
class="button-default dropdown-item dropdown-item-icon"
@click="openTab('general')"
>
<FAIcon icon="wrench" />{{ $t('settings.more_settings') }}
</button>
</div>
<div slot="trigger">
<FAIcon icon="filter" />
</div>
</Popover> </Popover>
</template> </template>

View file

@ -9,28 +9,26 @@
@show="openMenu" @show="openMenu"
@close="() => isOpen = false" @close="() => isOpen = false"
> >
<div <template v-slot:content>
slot="content" <div class="timeline-menu-popover popover-default">
class="timeline-menu-popover panel panel-default" <TimelineMenuContent />
> </div>
<TimelineMenuContent /> </template>
</div> <template v-slot:trigger>
<div <button class="button-unstyled title timeline-menu-title">
slot="trigger" <span class="timeline-title">{{ timelineName() }}</span>
class="title timeline-menu-title" <span>
> <FAIcon
<span class="timeline-title">{{ timelineName() }}</span> size="sm"
<span> icon="chevron-down"
<FAIcon />
size="sm" </span>
icon="chevron-down" <span
class="click-blocker"
@click="blockOpen"
/> />
</span> </button>
<span </template>
class="click-blocker"
@click="blockOpen"
/>
</div>
</Popover> </Popover>
</template> </template>

View file

@ -53,17 +53,17 @@
> >
{{ user.name }} {{ user.name }}
</div> </div>
<a <button
v-if="isOtherUser && !user.is_local" v-if="isOtherUser && !user.is_local"
:href="user.statusnet_profile_url" :href="user.statusnet_profile_url"
target="_blank" target="_blank"
class="external-link-button" class="button-unstyled external-link-button"
> >
<FAIcon <FAIcon
class="icon" class="icon"
icon="external-link-alt" icon="external-link-alt"
/> />
</a> </button>
<AccountActions <AccountActions
v-if="isOtherUser && loggedIn" v-if="isOtherUser && loggedIn"
:user="user" :user="user"

View file

@ -4,40 +4,39 @@
placement="top" placement="top"
:offset="{ y: 5 }" :offset="{ y: 5 }"
> >
<template slot="trigger"> <template v-slot:trigger>
<slot /> <slot />
</template> </template>
<div <template v-slot:content>
slot="content" <div class="user-list-popover">
class="user-list-popover" <template v-if="users.length">
> <div
<div v-if="users.length"> v-for="(user) in usersCapped"
<div :key="user.id"
v-for="(user) in usersCapped" class="user-list-row"
:key="user.id" >
class="user-list-row" <UserAvatar
> :user="user"
<UserAvatar class="avatar-small"
:user="user" :compact="true"
class="avatar-small" />
:compact="true" <div class="user-list-names">
/> <!-- eslint-disable vue/no-v-html -->
<div class="user-list-names"> <span v-html="user.name_html" />
<!-- eslint-disable vue/no-v-html --> <!-- eslint-enable vue/no-v-html -->
<span v-html="user.name_html" /> <span class="user-list-screen-name">{{ user.screen_name_ui }}</span>
<!-- eslint-enable vue/no-v-html --> </div>
<span class="user-list-screen-name">{{ user.screen_name_ui }}</span>
</div> </div>
</div> </template>
<template v-else>
<FAIcon
icon="circle-notch"
spin
size="3x"
/>
</template>
</div> </div>
<div v-else> </template>
<FAIcon
icon="circle-notch"
spin
size="3x"
/>
</div>
</div>
</Popover> </Popover>
</template> </template>

View file

@ -60,10 +60,7 @@
:disabled="!user.friends_count" :disabled="!user.friends_count"
> >
<FriendList :user-id="userId"> <FriendList :user-id="userId">
<template <template v-slot:item="{item}">
slot="item"
slot-scope="{item}"
>
<FollowCard :user="item" /> <FollowCard :user="item" />
</template> </template>
</FriendList> </FriendList>
@ -75,10 +72,7 @@
:disabled="!user.followers_count" :disabled="!user.followers_count"
> >
<FollowerList :user-id="userId"> <FollowerList :user-id="userId">
<template <template v-slot:item="{item}">
slot="item"
slot-scope="{item}"
>
<FollowCard <FollowCard
:user="item" :user="item"
:no-follows-you="isUs" :no-follows-you="isUs"

View file

@ -45,10 +45,7 @@
</div> </div>
<div class="user-reporting-panel-right"> <div class="user-reporting-panel-right">
<List :items="statuses"> <List :items="statuses">
<template <template v-slot:item="{item}">
slot="item"
slot-scope="{item}"
>
<div class="status-fadein user-reporting-panel-sitem"> <div class="status-fadein user-reporting-panel-sitem">
<Status <Status
:in-conversation="false" :in-conversation="false"