Merge branch 'develop' into shigusegubu-themes3

This commit is contained in:
Henry Jameson 2026-08-04 22:21:41 +03:00
commit 5e2cde3999
98 changed files with 358 additions and 489 deletions

View file

@ -35,7 +35,7 @@ const getAllAccessibleAnnotations = async (projectRoot) => {
}),
)
)
.filter((k) => k)
.filter(Boolean)
.join(',\n')
return `

View file

@ -3,7 +3,7 @@ import emojis from '@kazvmoe-infra/unicode-emoji-json/data-by-group.json' with {
type: 'json',
}
Object.keys(emojis).map((k) => {
Object.keys(emojis).forEach((k) => {
emojis[k].forEach((e) => {
delete e.unicode_version
delete e.emoji_version

View file

@ -1,3 +1,5 @@
sonar.projectKey=Pleroma-FE
sonar.qualitygatesonar.host.url.wait=true
sonar.host.url=https://sonarqube.pleroma.dev
sonar.sources=src,build,tools
sonar.tests=test

View file

@ -237,10 +237,10 @@ export const changeStatusScope = ({
credentials,
}) => {
const payload = {}
if (typeof sensitive !== 'undefined') {
if (sensitive !== undefined) {
payload['sensitive'] = sensitive
}
if (typeof visibility !== 'undefined') {
if (visibility !== undefined) {
payload['visibility'] = visibility
}
@ -260,15 +260,15 @@ export const announcementToPayload = ({
}) => {
const payload = { content }
if (typeof startsAt !== 'undefined') {
if (startsAt !== undefined) {
payload.starts_at = startsAt ? new Date(startsAt).toISOString() : null
}
if (typeof endsAt !== 'undefined') {
if (endsAt !== undefined) {
payload.ends_at = endsAt ? new Date(endsAt).toISOString() : null
}
if (typeof allDay !== 'undefined') {
if (allDay !== undefined) {
payload.all_day = allDay
}

View file

@ -18,7 +18,7 @@ export const chats = ({ credentials }) =>
url: PLEROMA_CHATS_URL,
credentials,
}).then(({ data }) => ({
data: data.map(parseChat).filter((c) => c),
data: data.map(parseChat).filter(Boolean),
}))
export const getOrCreateChat = ({ accountId, credentials }) =>
@ -40,7 +40,7 @@ export const chatMessages = ({
method: 'GET',
credentials,
}).then(({ data }) => ({
data: data.map(parseChatMessage).filter((c) => c),
data: data.map(parseChatMessage).filter(Boolean),
}))
}

View file

@ -37,11 +37,7 @@ export const paramsString = (params = {}) => {
arrays.forEach(([k, array]) => {
array.forEach((v) => {
if (
typeof v === 'object' ||
typeof v === 'function' ||
typeof v === 'undefined'
)
if (typeof v === 'object' || typeof v === 'function' || v === undefined)
throw new TypeError('Array param cannot contain non-primitives!')
})
})

View file

@ -29,9 +29,8 @@ const Announcement = {
currentUser: (state) => state.users.currentUser,
}),
canEditAnnouncement() {
return (
this.currentUser &&
this.currentUser.privileges.has('announcements_manage_announcements')
return this.currentUser?.privileges.has(
'announcements_manage_announcements',
)
},
content() {

View file

@ -33,9 +33,8 @@ const AnnouncementsPage = {
return useAnnouncementsStore().announcements
},
canPostAnnouncement() {
return (
this.currentUser &&
this.currentUser.privileges.has('announcements_manage_announcements')
return this.currentUser?.privileges.has(
'announcements_manage_announcements',
)
},
},

View file

@ -165,16 +165,16 @@ const Attachment = {
useMediaViewerStore().setCurrentMedia(this.attachment)
},
onEdit(event) {
this.edit && this.edit(this.attachment, event)
this.edit?.(this.attachment, event)
},
onRemove() {
this.remove && this.remove(this.attachment)
this.remove?.(this.attachment)
},
onShiftUp() {
this.shiftUp && this.shiftUp(this.attachment)
this.shiftUp?.(this.attachment)
},
onShiftDn() {
this.shiftDn && this.shiftDn(this.attachment)
this.shiftDn?.(this.attachment)
},
stopFlash() {
this.$refs.flash.closePlayer()

View file

@ -1,9 +1,9 @@
<template>
<div
v-if="isMessage"
:id="`chatmessage-${message.id}`"
class="chat-message-wrapper"
:class="[classnames, { 'hovered-message-chain': hoveredMessageChain }]"
:id="`chatmessage-${message.id}`"
@mouseover="onHover(true)"
@mouseleave="onHover(false)"
>
@ -40,7 +40,10 @@
</template>
</i18n-t>
</StatusPopover>
<span v-else class="reply-label">
<span
v-else
class="reply-label"
>
{{ $t('status.broken_reply') }}
</span>
</template>
@ -83,7 +86,10 @@
:user="author"
/>
</UserPopover>
<div v-else class="avatar-spacer" />
<div
v-else
class="avatar-spacer"
/>
</div>
<div class="chat-message-inner">
<div class="message-bubble-wrapper">
@ -98,7 +104,6 @@
@mouseenter="hovered = true"
@mouseleave="hovered = false"
>
<StatusActionButtons
v-if="isStatus"
class="chat-message-toolbar"
@ -112,9 +117,9 @@
@toggle-replying="$emit('replyRequested', message)"
/>
<div
v-else
class="chat-message-toolbar"
:class="{ '-visible': hovered || menuOpened }"
v-else
>
<Popover
trigger="click"
@ -221,7 +226,10 @@
v-if="isStatus && repliedTo"
class="reply-indicator"
>
<FAIcon class="icon" icon="reply" />
<FAIcon
class="icon"
icon="reply"
/>
</div>
<div class="end-spacer" />
</div>
@ -232,7 +240,10 @@
v-else
class="chat-message-date-separator"
>
<ChatMessageDate :date="chatItem.date" :show-time="chatItem.isTime" />
<ChatMessageDate
:date="chatItem.date"
:show-time="chatItem.isTime"
/>
</div>
</template>

View file

@ -7,7 +7,7 @@
:previous-item="getPreviousItem(index)"
:hovered-message-chain="chatItem.messageChainId === hoveredMessageChainId"
:focused="chatItem.id === focusedId"
:repliedTo="chatItem.id === repliedId"
:replied-to="chatItem.id === repliedId"
@hover="onMessageHover"
@delete="onMessageDelete"
@reply-requested="onReplyRequested"

View file

@ -97,7 +97,7 @@ const Chat = {
mounted() {
window.addEventListener('resize', this.handleResize)
window.addEventListener('scroll', this.handleScroll)
if (typeof document.hidden !== 'undefined') {
if (document.hidden !== undefined) {
document.addEventListener(
'visibilitychange',
this.handleVisibilityChange,
@ -112,7 +112,7 @@ const Chat = {
unmounted() {
window.removeEventListener('scroll', this.handleScroll)
window.removeEventListener('resize', this.handleResize)
if (typeof document.hidden !== 'undefined')
if (document.hidden !== undefined)
document.removeEventListener(
'visibilitychange',
this.handleVisibilityChange,

View file

@ -24,7 +24,7 @@
v-if="messages[0]?.summary_raw_html"
:html="messages[0].summary_raw_html"
:emoji="messages[0].emojis"
/>
/>
<template v-else>
{{ $t('timeline.conversation') }}
</template>

View file

@ -11,11 +11,11 @@
{{ label }}
</label>
<Checkbox
v-if="typeof fallback !== 'undefined' && showOptionalCheckbox && !hideOptionalCheckbox"
v-if="fallback !== undefined && showOptionalCheckbox && !hideOptionalCheckbox"
:model-value="present"
:disabled="disabled"
class="opt"
@update:model-value="updateValue(typeof modelValue === 'undefined' ? fallback : undefined)"
@update:model-value="updateValue(modelValue === undefined ? fallback : undefined)"
/>
<div
class="input color-input-field"

View file

@ -62,7 +62,7 @@ const sortAndFilterConversation = (conversation, statusoid) => {
} else {
conversation = filter(conversation, (status) => status.type !== 'retweet')
}
return conversation.filter((_) => _).sort(sortById)
return conversation.filter(Boolean).sort(sortById)
}
const conversation = {
@ -239,9 +239,9 @@ const conversation = {
depth,
},
walk(forest, forest[id], depth + 1, processed),
].reduce((a, b) => a.concat(b), [])
].flat()
})
.reduce((a, b) => a.concat(b), [])
.flat()
const linearized = walk(
threads.forest,
@ -305,11 +305,10 @@ const conversation = {
topLevel() {
const topLevel = this.conversation.reduce(
(tl, cur) =>
tl.filter(
(k) =>
this.getReplies(cur.id)
.map((v) => v.id)
.indexOf(k.id) === -1,
tl.filter((k) =>
this.getReplies(cur.id)
.map((v) => v.id)
.includes(k.id),
),
this.conversation,
)
@ -375,7 +374,7 @@ const conversation = {
return !!(this.expanded || this.isPage)
},
hiddenStyle() {
const height = (this.status && this.status.virtualHeight) || '120px'
const height = this.status?.virtualHeight || '120px'
return this.virtualHidden ? { height } : {}
},
threadDisplayStatus() {

View file

@ -14,9 +14,9 @@
v-if="conversation[0]?.summary_raw_html"
:html="conversation[0].summary_raw_html"
:emoji="conversation[0].emojis"
/>
/>
<template v-else>
{{ $t('timeline.conversation') }}
{{ $t('timeline.conversation') }}
</template>
</h1>
<button

View file

@ -45,8 +45,7 @@ export default {
data: () => ({
searchBarHidden: true,
supportsMask:
window.CSS &&
window.CSS.supports &&
window.CSS?.supports &&
(window.CSS.supports('mask-size', 'contain') ||
window.CSS.supports('-webkit-mask-size', 'contain') ||
window.CSS.supports('-moz-mask-size', 'contain') ||

View file

@ -42,9 +42,7 @@ const EditStatusModal = {
},
isFormVisible(val) {
if (val) {
this.$nextTick(
() => this.$el && this.$el.querySelector('textarea').focus(),
)
this.$nextTick(() => this.$el?.querySelector('textarea').focus())
}
},
},

View file

@ -188,8 +188,8 @@ const EmojiInput = {
}
return {
names: names.filter((k) => k),
keywords: keywords.filter((k) => k),
names: names.filter(Boolean),
keywords: keywords.filter(Boolean),
}
}
},

View file

@ -79,7 +79,7 @@ export const suggestUsers = ({ dispatch, state }) => {
const userSearch = (query) => dispatch('searchUsers', { query })
const debounceUserSearch = (query) => {
cancelUserSearch && cancelUserSearch()
cancelUserSearch?.()
return new Promise((resolve, reject) => {
timeout = setTimeout(() => {
userSearch(query).then(resolve).catch(reject)

View file

@ -57,7 +57,7 @@ const maybeLocalizedKeywords = (emoji, languages, nameLocalizer) => {
languages.forEach((lang) => {
const keywords = emoji.annotations[lang]?.keywords || []
const name = emoji.annotations[lang]?.name
res.push(...keywords.concat([name]).filter((k) => k))
res.push(...keywords.concat([name]).filter(Boolean))
})
}
return res
@ -408,7 +408,7 @@ const EmojiPicker = {
isFirstRow: index === 0,
})),
)
.reduce((a, c) => a.concat(c), [])
.flat()
},
languages() {
return ensureFinalFallback(

View file

@ -44,7 +44,7 @@ const Flash = {
})
},
closePlayer() {
this.ruffleInstance && this.ruffleInstance.remove()
this.ruffleInstance?.remove()
this.player = false
this.$emit('playerClosed')
},

View file

@ -35,7 +35,7 @@ export default {
'sans-serif',
'monospace',
...(this.options || []),
].filter((_) => _),
].filter(Boolean),
}
},
methods: {

View file

@ -2,7 +2,7 @@
<div class="font-control">
<div class="setting-item">
<Checkbox
v-if="typeof fallback !== 'undefined'"
v-if="fallback !== undefined"
:id="name + '-o'"
class="font-checkbox setting-control setting-label"
:model-value="present"

View file

@ -50,7 +50,7 @@ const ImageCropper = {
},
readFile() {
const fileInput = this.$refs.input
if (fileInput.files != null && fileInput.files[0] != null) {
if (fileInput?.files?.[0]) {
this.file = fileInput.files[0]
const reader = new window.FileReader()
reader.onload = (e) => {

View file

@ -59,12 +59,12 @@ const ListsNew = {
membersUsers() {
return [...this.membersUserIds, ...this.addedUserIds]
.map((userId) => this.findUser(userId))
.filter((user) => user)
.filter(Boolean)
},
searchUsers() {
return this.searchUserIds
.map((userId) => this.findUser(userId))
.filter((user) => user)
.filter(Boolean)
},
...mapState({
currentUser: (state) => state.users.currentUser,

View file

@ -75,13 +75,11 @@ const MentionLink = {
},
computed: {
user() {
return (
this.url && this.$store && this.$store.getters.findUserByUrl(this.url)
)
return this.url && this.$store?.getters.findUserByUrl(this.url)
},
isYou() {
// FIXME why user !== currentUser???
return this.user && this.user.id === this.currentUser.id
return this.user?.id === this.currentUser.id
},
userName() {
return this.user && this.userNameFullUi.split('@')[0]
@ -94,10 +92,10 @@ const MentionLink = {
)
},
userNameFull() {
return this.user && this.user.screen_name
return this.user?.screen_name
},
userNameFullUi() {
return this.user && this.user.screen_name_ui
return this.user?.screen_name_ui
},
highlightData() {
return this.highlight[this.user?.screen_name]

View file

@ -405,7 +405,7 @@ const ModerationTools = {
)
},
isAdmin() {
this.$store.state.users.currentUser.role === 'admin'
return this.$store.state.users.currentUser.role === 'admin'
},
},
methods: {

View file

@ -11,7 +11,7 @@ import { useInstanceStore } from 'src/stores/instance.js'
*/
const toInstanceReasonObject = (instances, info, key) => {
return instances.map((instance) => {
if (info[key] && info[key][instance] && info[key][instance].reason) {
if (info[key]?.[instance]?.reason) {
return { instance, reason: info[key][instance].reason }
}
return { instance, reason: '' }

View file

@ -15,8 +15,7 @@ export const filterNavigation = (
if (!isFederating && set.has('federating')) return false
if (!currentUser && isPrivate && set.has('!private')) return false
if (!currentUser && !(anon || anonRoute)) return false
if ((!currentUser || !currentUser.locked) && set.has('lockedUser'))
return false
if (!currentUser?.locked && set.has('lockedUser')) return false
if (!hasChats && set.has('chats')) return false
if (!hasAnnouncements && set.has('announcements')) return false
if (!supportsBubbleTimeline && set.has('supportsBubbleTimeline'))

View file

@ -11,7 +11,7 @@
{{ label || $t('settings.style.themes3.editor.opacity') }}
</label>
<Checkbox
v-if="typeof fallback !== 'undefined'"
v-if="fallback !== undefined"
:model-value="present"
:disabled="disabled"
class="opt"

View file

@ -39,13 +39,13 @@ export default {
return storePoll || {}
},
options() {
return (this.poll && this.poll.options) || []
return this.poll?.options || []
},
expiresAt() {
return (this.poll && this.poll.expires_at) || null
return this.poll?.expires_at || null
},
expired() {
return (this.poll && this.poll.expired) || false
return this.poll?.expired || false
},
expirationLabel() {
if (useMergedConfigStore().mergedConfig.useAbsoluteTimeFormat) {

View file

@ -129,9 +129,7 @@ const Popover = {
// Popover will be anchored around this element, trigger ref is the container, so
// its children are what are inside the slot. Expect only one v-slot:trigger.
const anchorEl =
this.anchorEl ||
(this.$refs.trigger && this.$refs.trigger.children[0]) ||
this.$el
this.anchorEl || this.$refs.trigger?.children[0] || this.$el
// SVGs don't have offsetWidth/Height, use fallback
const anchorHeight = anchorEl.offsetHeight || anchorEl.clientHeight
const anchorWidth = anchorEl.offsetWidth || anchorEl.clientWidth
@ -155,8 +153,7 @@ const Popover = {
// Minor optimization, don't call a slow reflow call if we don't have to
const parentScreenBox =
this.boundTo &&
(this.boundTo.x === 'container' || this.boundTo.y === 'container') &&
(this.boundTo?.x === 'container' || this.boundTo?.y === 'container') &&
this.containerBoundingClientRect()
const margin = this.margin || {}
@ -164,7 +161,7 @@ const Popover = {
// What are the screen bounds for the popover? Viewport vs container
// when using viewport, using default margin values to dodge the navbar
const xBounds =
this.boundTo && this.boundTo.x === 'container'
this.boundTo?.x === 'container'
? {
min: parentScreenBox.left + (margin.left || 0),
max: parentScreenBox.right - (margin.right || 0),
@ -175,7 +172,7 @@ const Popover = {
}
const yBounds =
this.boundTo && this.boundTo.y === 'container'
this.boundTo?.y === 'container'
? {
min: parentScreenBox.top + (margin.top || 0),
max: parentScreenBox.bottom - (margin.bottom || 0),
@ -247,12 +244,12 @@ const Popover = {
if (bottomBoundary + content.offsetHeight > yBounds.max) usingTop = true
if (topBoundary - content.offsetHeight < yBounds.min) usingTop = false
const yOffset = (this.offset && this.offset.y) || 0
const yOffset = this.offset?.y || 0
translateY = usingTop
? topBoundary - yOffset - content.offsetHeight
: bottomBoundary + yOffset
const xOffset = (this.offset && this.offset.x) || 0
const xOffset = this.offset?.x || 0
translateX = origin.x + horizOffset + xOffset
} else {
// Default to whatever user wished with placement prop
@ -268,12 +265,12 @@ const Popover = {
if (rightBoundary + content.offsetWidth > xBounds.max) usingLeft = true
if (leftBoundary - content.offsetWidth < xBounds.min) usingLeft = false
const xOffset = (this.offset && this.offset.x) || 0
const xOffset = this.offset?.x || 0
translateX = usingLeft
? leftBoundary - xOffset - content.offsetWidth
: rightBoundary + xOffset
const yOffset = (this.offset && this.offset.y) || 0
const yOffset = this.offset?.y || 0
translateY = origin.y + vertOffset + yOffset
}
@ -298,7 +295,7 @@ const Popover = {
}, 0)
const wasHidden = this.hidden
this.hidden = false
this.parentPopover && this.parentPopover.onChildPopoverState(this, true)
this.parentPopover?.onChildPopoverState(this, true)
if (this.trigger === 'click' || this.stayOnClick) {
document.addEventListener('click', this.onClickOutside)
}
@ -316,7 +313,7 @@ const Popover = {
if (this.disabled) return
if (!this.hidden) this.$emit('close')
this.hidden = true
this.parentPopover && this.parentPopover.onChildPopoverState(this, false)
this.parentPopover?.onChildPopoverState(this, false)
if (this.trigger === 'click') {
document.removeEventListener('click', this.onClickOutside)
}
@ -366,7 +363,7 @@ const Popover = {
onClickOutside(e) {
if (this.disableClickOutside) return
if (this.hidden) return
if (this.$refs.content && this.$refs.content.contains(e.target)) return
if (this.$refs.content?.contains(e.target)) return
if (this.$el.contains(e.target)) return
if (this.childrenShown.size > 0) return
this.hidePopover()

View file

@ -316,7 +316,7 @@ const PostStatusForm = {
},
// -Edit
isEdit() {
return typeof this.statusId !== 'undefined' && this.statusId.trim() !== ''
return this.statusId !== undefined && this.statusId.trim() !== ''
},
// -Reply
isReply() {
@ -619,7 +619,7 @@ const PostStatusForm = {
this.newStatus.quote = null
this.newStatus.nsfw = this.defaultNewStatus.nsfw
this.newStatus.mediaDescriptions = {}
this.$refs.mediaUpload && this.$refs.mediaUpload.clearFile()
this.$refs.mediaUpload?.clearFile()
if (this.preserveFocus) {
this.$nextTick(() => {
this.$refs.textarea.focus()
@ -809,7 +809,7 @@ const PostStatusForm = {
}
},
fileDrop(e) {
if (e.dataTransfer && e.dataTransfer.types.includes('Files')) {
if (e.dataTransfer?.types.includes('Files')) {
e.preventDefault() // allow dropping text like before
this.dropFiles = e.dataTransfer.files
clearTimeout(this.dropStopTimeout)
@ -826,7 +826,7 @@ const PostStatusForm = {
},
fileDrag(e) {
e.dataTransfer.dropEffect = this.uploadFileLimitReached ? 'none' : 'copy'
if (e.dataTransfer && e.dataTransfer.types.includes('Files')) {
if (e.dataTransfer?.types.includes('Files')) {
clearTimeout(this.dropStopTimeout)
this.showDropIcon = 'show'
}

View file

@ -1,8 +1,8 @@
<template>
<div
v-if="initialized"
ref="form"
class="post-status-form"
v-if="initialized"
>
<form
autocomplete="off"
@ -176,12 +176,12 @@
<input
v-if="mentionsLine"
:value="mentionsLineReadOnly ? mentionsString : newStatus.mentionsLine"
@change="onMentionsLineUpdate"
type="text"
:placeholder="$t('post_status.mentions_line')"
:disabled="mentionsLineReadOnly || (posting && !optimisticPosting)"
size="1"
class="input mentions-input form-post-mentions unstyled"
@change="onMentionsLineUpdate"
>
<EmojiInput
ref="emoji-input"
@ -271,14 +271,14 @@
<PollForm
v-if="pollsAvailable"
ref="pollForm"
:visible="pollFormVisible"
v-model="newStatus.poll"
:visible="pollFormVisible"
/>
<QuoteForm
v-if="quotingAvailable"
:id="newStatus.quote?.id"
ref="quoteForm"
:visible="quoteFormVisible"
:id="newStatus.quote?.id"
:url="newStatus.quote?.url"
@update:url="url => newStatus.quote.url = url"
@update:id="id => newStatus.quote.id = id"

View file

@ -40,9 +40,7 @@ const PostStatusModal = {
},
isFormVisible(val) {
if (val) {
this.$nextTick(
() => this.$el && this.$el.querySelector('textarea').focus(),
)
this.$nextTick(() => this.$el?.querySelector('textarea').focus())
}
},
},

View file

@ -102,7 +102,7 @@ export default {
type: 'statuses',
})
.then((data) => {
if (data?.statuses && data.statuses.length === 1) {
if (data?.statuses?.length === 1) {
this.$emit('update:id', data.statuses[0].id)
} else {
this.handleError(true)

View file

@ -11,7 +11,7 @@
{{ label }}
</label>
<input
v-if="typeof fallback !== 'undefined'"
v-if="fallback !== undefined"
:id="name + '-o'"
:aria-labelledby="name + '-label'"
class="input -checkbox opt visible-for-screenreader-only"
@ -20,7 +20,7 @@
@change="$emit('update:modelValue', !present ? fallback : undefined)"
>
<label
v-if="typeof fallback !== 'undefined'"
v-if="fallback !== undefined"
class="opt-l"
:for="name + '-o'"
:aria-hidden="true"

View file

@ -128,7 +128,7 @@ const registration = {
this.user.captcha_answer_data = this.captcha.answer_data
if (this.user.language) {
this.user.language = localeService.internalToBackendLocaleMulti(
this.user.language.filter((k) => k),
this.user.language.filter(Boolean),
)
}
@ -154,7 +154,7 @@ const registration = {
})
},
replaceNewlines(str) {
return str.replace(/\s*\n\s*/g, ' \n')
return str.replaceAll(/\s*\n\s*/g, ' \n')
},
},
}

View file

@ -181,7 +181,7 @@ export default {
}
// Processor to use with html_tree_converter
const processItem = (item, index, array, what) => {
const processItem = (item, index, array) => {
// Handle text nodes - just add emoji
if (typeof item === 'string') {
const emptyText = item.trim() === ''
@ -251,18 +251,14 @@ export default {
return ['', [mentionsLinePadding, renderImage(opener)], '']
} else if (Tag === 'a' && this.handleLinks) {
// replace mentions with MentionLink
if (fullAttrs.class && fullAttrs.class.includes('mention')) {
if (fullAttrs.class?.includes('mention')) {
// Handling mentions here
return renderMention(attrs, children)
} else {
currentMentions = null
}
} else if (Tag === 'span') {
if (
this.handleLinks &&
fullAttrs.class &&
fullAttrs.class.includes('h-card')
) {
if (this.handleLinks && fullAttrs.class?.includes('h-card')) {
return ['', children.map(processItem), '']
}
}
@ -281,7 +277,7 @@ export default {
// Processor for back direction (for finding "last" stuff, just easier this way)
let encounteredTextReverse = false
const processItemReverse = (item, index, array, what) => {
const processItemReverse = (item, index, array) => {
// Handle text nodes - just add emoji
if (typeof item === 'string') {
const emptyText = item.trim() === ''
@ -300,7 +296,7 @@ export default {
const attrs = getAttrs(opener, () => true)
// should only be this
if (
(fullAttrs.class && fullAttrs.class.includes('hashtag')) || // Pleroma style
fullAttrs.class?.includes('hashtag') || // Pleroma style
fullAttrs.rel === 'tag' // Mastodon style
) {
return renderHashtag(attrs, children, encounteredTextReverse)
@ -385,13 +381,13 @@ export default {
x ? 'mfm-spinX' : null,
y ? 'mfm-spinY' : null,
'mfm-spin',
].filter((a) => a)[0]
].filter(Boolean)[0]
const direction = [
alternate ? 'alternate' : null,
left ? 'reverse' : null,
'normal',
].filter((a) => a)[0]
].filter(Boolean)[0]
newAttrs.style = [
`animation-name: ${anim}`,
@ -479,7 +475,7 @@ export default {
>
{this.collapse
? pass2.map((x) => {
if (typeof x === 'string') return x.replace(/\n/g, ' ')
if (typeof x === 'string') return x.replaceAll('\n', ' ')
if (!Array.isArray(x)) return x
return x.map((y) => (y.type === 'br' ? ' ' : y))
})
@ -547,8 +543,8 @@ export const preProcessPerLine = (html, greentext) => {
(string.includes('&gt;') || string.includes('&lt;'))
) {
const cleanedString = string
.replace(/<[^>]+?>/gi, '') // remove all tags
.replace(/@\w+/gi, '') // remove mentions (even failed ones)
.replaceAll(/<[^>]+?>/gi, '') // remove all tags
.replaceAll(/@\w+/gi, '') // remove mentions (even failed ones)
.trim()
if (cleanedString.startsWith('&gt;')) {
return `<span class='greentext'>${string}</span>`

View file

@ -11,7 +11,7 @@
{{ label }}
</label>
<Checkbox
v-if="typeof fallback !== 'undefined'"
v-if="fallback !== undefined"
:model-value="present"
:disabled="disabled"
class="opt"

View file

@ -122,7 +122,7 @@ const Search = {
return 'statuses'
},
lastHistoryRecord(hashtag) {
return hashtag.history && hashtag.history[0]
return hashtag.history?.[0]
},
},
}

View file

@ -122,7 +122,7 @@ const EmojiTab = {
return this.refreshPackList()
} else {
this.displayError(resp.error)
return Promise.reject(resp)
throw new Error(resp)
}
})
.then(() => {
@ -139,7 +139,7 @@ const EmojiTab = {
return this.refreshPackList()
} else {
this.displayError(resp.error)
return Promise.reject(resp)
throw new Error(resp)
}
})
.then(() => {
@ -239,7 +239,7 @@ const EmojiTab = {
return this.refreshPackList()
} else {
this.displayError(resp.error)
return Promise.reject(resp)
throw new Error(resp)
}
})
.then(() => {
@ -259,7 +259,7 @@ const EmojiTab = {
return this.refreshPackList()
} else {
this.displayError(resp.error)
return Promise.reject(resp)
throw new Error(resp)
}
})
.then(() => {
@ -280,7 +280,7 @@ const EmojiTab = {
return this.refreshPackList()
} else {
this.displayError(resp.error)
return Promise.reject(resp)
throw new Error(resp)
}
})
.then(() => {
@ -300,7 +300,7 @@ const EmojiTab = {
sortPackFiles(nameOfPack) {
// Sort by key
const sorted = Object.keys(this.knownPacks[nameOfPack].files)
.sort()
.sort((a, b) => a.localeCompare(b))
.reduce((acc, key) => {
if (key.length === 0) return acc
acc[key] = this.knownPacks[nameOfPack].files[key]

View file

@ -262,7 +262,7 @@ export default {
.then((resp) => {
if (resp.error !== undefined) {
this.$emit('displayError', resp.error)
return Promise.reject(resp.error)
throw new Error(resp.error)
}
return resp.json()

View file

@ -28,12 +28,12 @@ export default {
methods: {
...Setting.methods,
getValue(e) {
if (!this.truncate === 1) {
if (this.truncate === 1) {
return Number.parseInt(e.target.value)
} else if (this.truncate > 1) {
return Math.trunc(e.target.value / this.truncate) * this.truncate
}
return parseFloat(e.target.value)
return Number.parseFloat(e.target.value)
},
},
}

View file

@ -179,7 +179,7 @@ export default {
[
'admin_dash',
'temp_overrides',
...this.canonPath.map((p) => p.replace(/\./g, '_DOT_')),
...this.canonPath.map((p) => p.replaceAll('.', '_DOT_')),
'label',
].join('.'),
)
@ -198,7 +198,7 @@ export default {
[
'admin_dash',
'temp_overrides',
...this.canonPath.map((p) => p.replace(/\./g, '_DOT_')),
...this.canonPath.map((p) => p.replaceAll('.', '_DOT_')),
'description',
].join('.'),
)

View file

@ -67,7 +67,10 @@ export default {
return this.$t(['settings', 'units', this.unitSet, value].join('.'))
},
updateValue(e) {
this.configSink(this.path, parseFloat(e.target.value) + this.stateUnit)
this.configSink(
this.path,
Number.parseFloat(e.target.value) + this.stateUnit,
)
},
updateUnit(e) {
let value = this.stateValue

View file

@ -236,7 +236,7 @@ const AppearanceTab = {
},
stylePalettes() {
const ruleset = useInterfaceStore().styleDataUsed || []
if (!ruleset?.length === 0) return
if (ruleset.length === 0) return
const meta = ruleset.find((x) => x.component === '@meta')
const result = ruleset
.filter((x) => x.component.startsWith('@palette'))
@ -257,7 +257,7 @@ const AppearanceTab = {
const result = {
name: `${meta.directives.name || this.$t('settings.style.themes3.palette.imported')}: ${variant}`,
key: `style.${variant.toLowerCase().replace(/ /g, '_')}`,
key: `style.${variant.toLowerCase().replaceAll(' ', '_')}`,
bg,
fg,
text,
@ -277,7 +277,7 @@ const AppearanceTab = {
return !window.IntersectionObserver
},
instanceWallpaper() {
useInstanceStore().instanceIdentity.background
return useInstanceStore().instanceIdentity.background
},
instanceWallpaperUsed() {
return (

View file

@ -1,5 +1,4 @@
import { mapActions, mapState } from 'pinia'
import { v4 as uuidv4 } from 'uuid'
import { mapState } from 'pinia'
import Checkbox from 'src/components/checkbox/checkbox.vue'
import Select from 'src/components/select/select.vue'
@ -12,7 +11,6 @@ import UnitSetting from '../helpers/unit_setting.vue'
import { useInstanceStore } from 'src/stores/instance.js'
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
import { useSyncConfigStore } from 'src/stores/sync_config.js'
const ClutterTab = {
components: {
@ -33,120 +31,6 @@ const ClutterTab = {
store.instanceIdentity.showInstanceSpecificPanel &&
store.instanceIdentity.instanceSpecificPanelContent,
}),
...mapState(useSyncConfigStore, {
muteFilters: (store) =>
Object.entries(store.prefsStorage.simple.muteFilters),
muteFiltersObject: (store) => store.prefsStorage.simple.muteFilters,
}),
},
methods: {
...mapActions(useSyncConfigStore, [
'setSimplePrefAndSave',
'unsetSimplePrefAndSave',
'pushSyncConfig',
]),
getDatetimeLocal(timestamp) {
const date = new Date(timestamp)
const fmt = new Intl.NumberFormat('en-US', { minimumIntegerDigits: 2 })
const datetime = [
date.getFullYear(),
'-',
fmt.format(date.getMonth() + 1),
'-',
fmt.format(date.getDate()),
'T',
fmt.format(date.getHours()),
':',
fmt.format(date.getMinutes()),
].join('')
return datetime
},
checkRegexValid(id) {
const filter = this.muteFiltersObject[id]
if (filter.type !== 'regexp') return true
if (filter.type !== 'user_regexp') return true
const { value } = filter
let valid = true
try {
new RegExp(value)
} catch {
valid = false
console.error('Invalid RegExp: ' + value)
}
return valid
},
createFilter(
filter = {
type: 'word',
value: '',
name: 'New Filter',
enabled: true,
expires: null,
hide: false,
},
) {
const newId = uuidv4()
filter.order = this.muteFilters.length + 2
this.muteFiltersDraftObject[newId] = filter
this.setSimplePrefAndSave({ path: 'muteFilters.' + newId, value: filter })
this.pushSyncConfig()
},
exportFilter(id) {
this.exportedFilter = { ...this.muteFiltersDraftObject[id] }
delete this.exportedFilter.order
this.filterExporter.exportData()
},
importFilter() {
this.filterImporter.importData()
},
copyFilter(id) {
const filter = { ...this.muteFiltersDraftObject[id] }
const newId = uuidv4()
this.muteFiltersDraftObject[newId] = filter
this.setSimplePrefAndSave({ path: 'muteFilters.' + newId, value: filter })
this.pushSyncConfig()
},
deleteFilter(id) {
delete this.muteFiltersDraftObject[id]
this.unsetSimplePrefAndSave({ path: 'muteFilters.' + id, value: null })
this.pushSyncConfig()
},
purgeExpiredFilters() {
this.muteFiltersExpired.forEach(([id]) => {
delete this.muteFiltersDraftObject[id]
this.unsetSimplePrefAndSave({ path: 'muteFilters.' + id, value: null })
})
this.pushSyncConfig()
},
updateFilter(id, field, value) {
const filter = { ...this.muteFiltersDraftObject[id] }
if (field === 'expires-never') {
if (!value) {
const offset = 1000 * 60 * 60 * 24 * 14 // 2 weeks
const date = Date.now() + offset
filter.expires = date
} else {
filter.expires = null
}
} else if (field === 'expires') {
const parsed = Date.parse(value)
filter.expires = parsed.valueOf()
} else {
filter[field] = value
}
this.muteFiltersDraftObject[id] = filter
this.muteFiltersDraftDirty[id] = true
},
saveFilter(id) {
this.setSimplePrefAndSave({
path: 'muteFilters.' + id,
value: this.muteFiltersDraftObject[id],
})
this.pushSyncConfig()
this.muteFiltersDraftDirty[id] = false
},
},
// Updating nested properties
watch: {

View file

@ -190,21 +190,24 @@ const FilteringTab = {
}
return valid
},
createFilter(
filter = {
createFilter(filter) {
const newId = uuidv4()
const newFilter = {
type: 'word',
value: '',
name: 'New Filter',
enabled: true,
expires: null,
hide: false,
},
) {
const newId = uuidv4()
...filter,
}
filter.order = this.muteFilters.length + 2
this.muteFiltersDraftObject[newId] = filter
this.setSimplePrefAndSave({ path: 'muteFilters.' + newId, value: filter })
newFilter.order = this.muteFilters.length + 2
this.muteFiltersDraftObject[newId] = newFilter
this.setSimplePrefAndSave({
path: 'muteFilters.' + newId,
value: newFilter,
})
},
exportFilter(id) {
this.exportedFilter = { ...this.muteFiltersDraftObject[id] }

View file

@ -319,7 +319,7 @@ export default {
return useInterfaceStore().themeDataUsed
},
shadowsAvailable() {
return Object.keys(DEFAULT_SHADOWS).sort()
return Object.keys(DEFAULT_SHADOWS).sort((a, b) => a.localeCompare(b))
},
currentShadowOverriden: {
get() {
@ -346,7 +346,7 @@ export default {
},
},
currentShadowFallback() {
return (this.previewTheme.shadows || {})[this.shadowSelected]
return this.previewTheme.shadows?.[this.shadowSelected]
},
currentShadow: {
get() {
@ -425,8 +425,8 @@ export default {
this.dismissWarning()
const version =
origin === 'localStorage' && !theme.colors ? 'l1' : fileVersion
const snapshotEngineVersion = (theme || {}).themeEngineVersion
const themeEngineVersion = (source || {}).themeEngineVersion || 2
const snapshotEngineVersion = theme?.themeEngineVersion
const themeEngineVersion = source?.themeEngineVersion || 2
const versionsMatch = themeEngineVersion === CURRENT_VERSION
const sourceSnapshotMismatch =
theme !== undefined &&
@ -611,7 +611,7 @@ export default {
*/
normalizeLocalState(theme, version = 0, source, forceSource = false) {
let input
if (typeof source !== 'undefined') {
if (source !== undefined) {
if (forceSource || source?.themeEngineVersion === CURRENT_VERSION) {
input = source
version = source.themeEngineVersion

View file

@ -181,14 +181,14 @@
name="accentColor"
:fallback="previewTheme.colors?.link"
:label="$t('settings.accent')"
:show-optional-checkbox="typeof linkColorLocal !== 'undefined'"
:show-optional-checkbox="linkColorLocal !== undefined"
/>
<ColorInput
v-model="linkColorLocal"
name="linkColor"
:fallback="previewTheme.colors?.accent"
:label="$t('settings.links')"
:show-optional-checkbox="typeof accentColorLocal !== 'undefined'"
:show-optional-checkbox="accentColorLocal !== undefined"
/>
<ContrastRatio :contrast="previewContrast.bgLink" />
</div>

View file

@ -61,7 +61,7 @@ const SideDrawer = {
this.toggleDrawer,
)
if (this.currentUser && this.currentUser.locked) {
if (this.currentUser?.locked) {
this.$store.dispatch('startFetchingFollowRequests')
}
},

View file

@ -128,8 +128,7 @@ const Status = {
computed: {
showReasonMutedThread() {
return (
(this.status.thread_muted ||
(this.status.reblog && this.status.reblog.thread_muted)) &&
(this.status.thread_muted || this.status.reblog?.thread_muted) &&
!this.inConversation
)
},
@ -263,7 +262,7 @@ const Status = {
this.muteFilterHits.length > 0 ? 'filtered' : null,
this.muteBotStatuses && this.botStatus ? 'bot' : null,
this.muteSensitiveStatuses && this.sensitiveStatus ? 'nsfw' : null,
].filter((_) => _)
].filter(Boolean)
},
muteLocalized() {
if (this.muteReasons.length === 0) return null

View file

@ -20,9 +20,9 @@
:get-component="getComponent"
:close="() => { /* no-op */ }"
:do-action="doAction"
@emoji-picker-shown="onEmojiPickerShown"
:default-button-style="useDefaultButtons"
:hide-label="hideLabels"
@emoji-picker-shown="onEmojiPickerShown"
/>
<button
v-if="showPin && currentUser"

View file

@ -147,16 +147,15 @@ const StatusBody = {
return this.status.attachments.map((file) => file.type)
},
collapsedStatus() {
return this.status.raw_html.replace(/(\n|<br\s?\/?>)/g, ' ')
return this.status.raw_html.replaceAll('(\n|<br\s?\/?>)', ' ')
},
...mapState(useMergedConfigStore, ['mergedConfig']),
},
mounted() {
this.status.attentions &&
this.status.attentions.forEach((attn) => {
const { id } = attn
this.$store.dispatch('fetchUserIfMissing', id)
})
this.status.attentions?.forEach((attn) => {
const { id } = attn
this.$store.dispatch('fetchUserIfMissing', id)
})
},
methods: {
onParseReady(event) {
@ -168,7 +167,7 @@ const StatusBody = {
.filter((mention) => !mention.notifying)
.forEach((mention) => {
const { content, url } = mention
const cleanedString = content.replace(/<[^>]+?>/gi, '') // remove all tags
const cleanedString = content.replaceAll(/<[^>]+?>/gi, '') // remove all tags
if (!cleanedString.startsWith('@')) return
const handle = cleanedString.slice(1)
const host = url.replace(/^https?:\/\//, '').replace(/\/.+?$/, '')

View file

@ -51,7 +51,7 @@ const StillImage = {
}
const image = this.$refs.src
if (!image) return
this.imageLoadHandler && this.imageLoadHandler(image)
this.imageLoadHandler?.(image)
const canvas = this.$refs.canvas
if (!canvas) return
const width = image.naturalWidth
@ -61,7 +61,7 @@ const StillImage = {
canvas.getContext('2d').drawImage(image, 0, 0, width, height)
},
onError() {
this.imageLoadError && this.imageLoadError()
this.imageLoadError?.()
},
},
watch: {

View file

@ -111,7 +111,11 @@ export default {
type="button"
role="tab"
>
<img src={props.image} title={props['image-tooltip']} />
<img
src={props.image}
alt={props['image-tooltip']}
title={props['image-tooltip']}
/>
{props.label ? '' : props.label}
</button>
</div>

View file

@ -196,7 +196,7 @@ export default {
},
computed: {
escapedNewBio() {
return ldEscape(this.newBio).replace(/\n/g, '<br>')
return ldEscape(this.newBio).replaceAll('\n', '<br>')
},
somethingToSave() {
if (this.newName !== this.user.name_unescaped) return true

View file

@ -1,7 +1,7 @@
#!/usr/bin/env node
const arg = process.argv[2]
if (typeof arg === 'undefined') {
if (arg === undefined) {
console.info('This is a very simple and tiny tool that checks en.json with any other language and')
console.info('outputs all the things present in english but missing in foreign language.')
console.info('')

View file

@ -20,7 +20,7 @@ const languageFileMap = import.meta.glob(['./*.json', '!./en.json'])
const loadLanguageFile = (code) => {
const jsonName = langCodeToJsonName(code)
if (jsonName === 'en') return Promise.resolve({ default: enMessages })
if (jsonName === 'en') return { default: enMessages }
return languageFileMap[`./${jsonName}.json`]()
}

View file

@ -74,7 +74,7 @@ export default function createPersistedState({
if (saveImmedeatelyActions.includes(mutation.type)) {
setState(key, reducer(cloneDeep(state), paths), storage).then(
(success) => {
if (typeof success !== 'undefined') {
if (success !== undefined) {
if (
mutation.type === 'setOption' ||
mutation.type === 'setCurrentUser'
@ -185,7 +185,7 @@ export const piniaPersistPlugin =
}
const fallbackValue = await storage.getItem(vuexKey)
if (fallbackValue && fallbackValue[id]) {
if (fallbackValue?.[id]) {
console.info(`Migrating ${id} store data from vuex to pinia`)
const res = fallbackValue[id]
await storage.setItem(key, res)
@ -198,7 +198,7 @@ export const piniaPersistPlugin =
const setState = (state) => {
if (!loadedGuard.loaded) {
console.info('waiting for old state to be loaded...')
return Promise.reject()
throw new Error('Waiting')
} else {
return storage.setItem(key, state)
}

View file

@ -325,7 +325,7 @@ const api = {
const token = state.wsToken
if (
useInstanceCapabilitiesStore().shoutAvailable &&
typeof token !== 'undefined' &&
token !== undefined &&
state.socket === null
) {
const socket = new Socket('/socket', { params: { token } })
@ -336,7 +336,7 @@ const api = {
}
},
disconnectFromSocket({ commit, state }) {
state.socket && state.socket.disconnect()
state.socket?.disconnect()
commit('setSocket', null)
},
},

View file

@ -49,8 +49,8 @@ const emptyTl = (userId = 0) => ({
visibleStatuses: [],
visibleStatusesObject: {},
newStatusCount: 0,
maxId: 0,
minId: 0,
maxId: '0',
minId: '0',
minVisibleId: 0,
loading: false,
followers: [],
@ -64,7 +64,7 @@ export const defaultState = () => ({
scrobblesNextFetch: {},
allStatusesObject: {},
conversationsObject: {},
maxId: 0,
maxId: '0',
favorites: new Set(),
timelines: {
mentions: emptyTl(),
@ -134,7 +134,7 @@ const sortById = (a, b) => {
const sortTimeline = (timeline) => {
timeline.visibleStatuses = timeline.visibleStatuses.sort(sortById)
timeline.statuses = timeline.statuses.sort(sortById)
timeline.minVisibleId = (last(timeline.visibleStatuses) || {}).id
timeline.minVisibleId = last(timeline.visibleStatuses)?.id
return timeline
}
@ -525,7 +525,7 @@ export const mutations = {
},
addRepeats(state, { id, rebloggedByUsers, currentUser }) {
const newStatus = state.allStatusesObject[id]
newStatus.rebloggedBy = rebloggedByUsers.filter((_) => _)
newStatus.rebloggedBy = rebloggedByUsers.filter(Boolean)
// repeats stats can be incorrect based on polling condition, let's update them using the most recent data
newStatus.repeat_num = newStatus.rebloggedBy.length
newStatus.repeated = !!newStatus.rebloggedBy.find(
@ -534,7 +534,7 @@ export const mutations = {
},
addFavs(state, { id, favoritedByUsers, currentUser }) {
const newStatus = state.allStatusesObject[id]
newStatus.favoritedBy = favoritedByUsers.filter((_) => _)
newStatus.favoritedBy = favoritedByUsers.filter(Boolean)
// favorites stats can be incorrect based on polling condition, let's update them using the most recent data
newStatus.fave_num = newStatus.favoritedBy.length
newStatus.favorited = !!newStatus.favoritedBy.find(
@ -879,7 +879,7 @@ const statuses = {
store.commit('addNewUsers', data.accounts)
store.commit(
'addNewUsers',
data.statuses.map((s) => s.user).filter((u) => u),
data.statuses.map((s) => s.user).filter(Boolean),
)
store.commit('addNewStatuses', {
statuses: data.statuses,

View file

@ -76,10 +76,10 @@ const mergeArrayLength = (oldValue, newValue) => {
const getNotificationPermission = () => {
const Notification = window.Notification
if (!Notification) return Promise.resolve(null)
if (!Notification) return null
if (Notification.permission === 'default')
return Notification.requestPermission()
return Promise.resolve(Notification.permission)
return Notification.permission
}
const blockUser = (store, args) => {
@ -269,7 +269,7 @@ export const mutations = {
state.currentUser.blockIds = blockIds
},
addBlockId(state, blockId) {
if (state.currentUser.blockIds.indexOf(blockId) === -1) {
if (state.currentUser.blockIds.includes(blockId)) {
state.currentUser.blockIds.push(blockId)
}
},
@ -283,7 +283,7 @@ export const mutations = {
state.currentUser.muteIdsMaxId = muteIdsMaxId
},
addMuteId(state, muteId) {
if (state.currentUser.muteIds.indexOf(muteId) === -1) {
if (state.currentUser.muteIds.includes(muteId)) {
state.currentUser.muteIds.push(muteId)
}
},
@ -291,7 +291,7 @@ export const mutations = {
state.currentUser.domainMutes = domainMutes
},
addDomainMute(state, domain) {
if (state.currentUser.domainMutes.indexOf(domain) === -1) {
if (state.currentUser.domainMutes.includes(domain)) {
state.currentUser.domainMutes.push(domain)
}
},
@ -388,7 +388,7 @@ const users = {
if (!user) {
return store.dispatch('fetchUser', id)
} else {
return Promise.resolve(user)
return user
}
},
updateUserAdminData(store, { userAdminData }) {
@ -635,7 +635,7 @@ const users = {
},
addNewNotifications(store, { notifications }) {
const users = map(notifications, 'from_profile')
const targetUsers = map(notifications, 'target').filter((_) => _)
const targetUsers = map(notifications, 'target').filter(Boolean)
const notificationIds = notifications.map((_) => _.id)
store.commit('addNewUsers', users)
store.commit('addNewUsers', targetUsers)

View file

@ -12,10 +12,7 @@ export const maybeShowChatNotification = (chat) => {
body: chat.lastMessage.content,
}
if (
chat.lastMessage.attachment &&
chat.lastMessage.attachment.type === 'image'
) {
if (chat.lastMessage.attachment?.type === 'image') {
opts.image = chat.lastMessage.attachment.preview_url
}

View file

@ -11,7 +11,7 @@ import { contrastRatio, convert, invertLightness } from 'chromatism'
* @param {Number} [b] - Blue component
*/
export const rgb2hex = (r, g, b) => {
if (r === null || typeof r === 'undefined') {
if (r === null || r === undefined) {
return undefined
}
// TODO: clean up this mess
@ -130,7 +130,7 @@ export const arithmeticBlend = (origin, value, operator) => {
* @returns {Object} sRGB of resulting color
*/
export const alphaBlend = (fg, fga, bg) => {
if (fga === 1 || typeof fga === 'undefined') {
if (fga === 1 || fga === undefined) {
return fg
}
@ -210,16 +210,16 @@ export const rgba2css = function (rgba) {
}
if (rgba !== null) {
if (rgba.r !== undefined && !isNaN(rgba.r)) {
if (rgba.r !== undefined && !Number.isNaN(rgba.r)) {
base.r = rgba.r
}
if (rgba.g !== undefined && !isNaN(rgba.g)) {
if (rgba.g !== undefined && !Number.isNaN(rgba.g)) {
base.g = rgba.g
}
if (rgba.b !== undefined && !isNaN(rgba.b)) {
if (rgba.b !== undefined && !Number.isNaN(rgba.b)) {
base.b = rgba.b
}
if (rgba.a !== undefined && !isNaN(rgba.a)) {
if (rgba.a !== undefined && !Number.isNaN(rgba.a)) {
base.a = rgba.a
}
} else {

View file

@ -62,8 +62,8 @@ export const parseUser = (data) => {
})
output.fields_text = data.fields.map((field) => {
return {
name: unescape(field.name.replace(/<[^>]*>/g, '')),
value: unescape(field.value.replace(/<[^>]*>/g, '')),
name: unescape(field.name.replaceAll(/<[^>]*>/g, '')),
value: unescape(field.value.replaceAll(/<[^>]*>/g, '')),
}
})
@ -191,7 +191,7 @@ export const parseUser = (data) => {
// Convert punycode to unicode for UI
output.screen_name_ui = output.screen_name
if (output.screen_name && output.screen_name.includes('@')) {
if (output.screen_name?.includes('@')) {
const parts = output.screen_name.split('@')
const unicodeDomain = punycode.toUnicode(parts[1])
if (unicodeDomain !== parts[1]) {

View file

@ -3,7 +3,7 @@ import { capitalize } from 'lodash'
function humanizeErrors(errors) {
return Object.entries(errors).reduce((errs, [k, val]) => {
const message = val.reduce((acc, message) => {
const key = capitalize(k.replace(/_/g, ' '))
const key = capitalize(k.replaceAll('_', ' '))
return acc + [key, message].join(' ') + '. '
}, '')
return [...errs, message]

View file

@ -43,7 +43,7 @@ export const fileTypeExt = (url) => {
}
export const fileMatchesSomeType = (types, file) =>
types.some((type) => fileType(file.mimetype) === type)
types.includes(fileType(file.mimetype))
const fileTypeService = {
fileType,

View file

@ -25,7 +25,7 @@ const visibleTypes = (notificationVisibility) => {
notificationVisibility.emojiReactions && 'pleroma:emoji_reaction',
notificationVisibility.reports && 'pleroma:report',
notificationVisibility.polls && 'poll',
].filter((_) => _)
].filter(Boolean)
}
const statusNotifications = new Set([
@ -95,9 +95,7 @@ export const filteredNotificationsFromStore = (
types,
) => {
// map is just to clone the array since sort mutates it and it causes some issues
const sortedNotifications = notificationsFromStore(store)
.map((_) => _)
.sort(sortById)
const sortedNotifications = notificationsFromStore(store).sort(sortById)
// TODO implement sorting elsewhere and make it optional
return sortedNotifications.filter((notification) =>
(types || visibleTypes(notificationVisibility)).includes(notification.type),
@ -175,13 +173,7 @@ export const prepareNotificationObject = (notification, i18n) => {
}
// Shows first attached non-nsfw image, if any. Should add configuration for this somehow...
if (
status &&
status.attachments &&
status.attachments.length > 0 &&
!status.nsfw &&
status.attachments[0].mimetype.startsWith('image/')
) {
if (!status.nsfw && status?.attachments?.[0]?.mimetype.startsWith('image/')) {
notifObj.image = status.attachments[0].url
}

View file

@ -66,7 +66,8 @@ const fetchAndUpdate = ({ store, credentials, older = false, sinceId }) => {
const unreadNotifsIds = notifications
.filter((n) => !n.seen)
.map((n) => n.id)
if (readNotifsIds.length > 0 && readNotifsIds.length > 0) {
if (readNotifsIds.length > 0 && unreadNotifsIds.length > 0) {
const minId = Math.min(...unreadNotifsIds) // Oldest known unread notification
if (minId !== Infinity) {
args.sinceId = null // Don't use since_id since it sorta conflicts with min_id

View file

@ -25,13 +25,13 @@ const createRuffleService = () => {
script.src = '/static/ruffle/ruffle.js'
script.type = 'text/javascript'
script.onerror = (e) => {
reject(e)
reject(new Error('Ruffle script errorred', e))
}
script.onabort = (e) => {
reject(e)
reject(new Error('Ruffle script aborted', e))
}
script.oncancel = (e) => {
reject(e)
reject(new Error('Ruffle script cancelled', e))
}
script.onload = () => {
ruffleInstance = window.RufflePlayer

View file

@ -88,5 +88,5 @@ export const muteFilterHits = (muteFilters, status) => {
}
}
})
.filter((_) => _)
.filter(Boolean)
}

View file

@ -29,7 +29,7 @@ export const createStyleSheet = (id, priority = 1000) => {
addRule(rule) {
let newRule = rule
if (!CSS.supports?.('backdrop-filter', 'blur()')) {
newRule = newRule.replace(/backdrop-filter:[^;]+;/g, '') // Remove backdrop-filter
newRule = newRule.replaceAll(/backdrop-filter:[^;]+;/g, '') // Remove backdrop-filter
}
if (newRule.startsWith('::-webkit')) {
@ -44,7 +44,7 @@ export const createStyleSheet = (id, priority = 1000) => {
}
this.rules.push(
newRule.replace(/var\(--shadowFilter\)[^;]*;/g, ''), // Remove shadowFilter references
newRule.replaceAll(/var\(--shadowFilter\)[^;]*;/g, ''), // Remove shadowFilter references
)
},
}
@ -316,7 +316,7 @@ export const getResourcesIndex = async (url, parser = (x) => x) => {
const resourceTransform = (resources) => {
return Object.entries(resources).map(([k, v]) => {
if (typeof v === 'object') {
return [k, () => Promise.resolve(v)]
return [k, () => v]
} else if (typeof v === 'string') {
return [
k,
@ -359,11 +359,9 @@ export const getResourcesIndex = async (url, parser = (x) => x) => {
const total = [...custom, ...builtin]
if (total.length === 0) {
return Promise.reject(
new Error(
`Resource at ${url} and ${customUrl} completely unavailable. Panicking`,
),
throw new Error(
`Resource at ${url} and ${customUrl} completely unavailable. Panicking`,
)
}
return Promise.resolve(Object.fromEntries(total))
return Object.fromEntries(total)
}

View file

@ -1,7 +1,9 @@
/* global process */
function urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 - (base64String.length % 4)) % 4)
const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/')
const base64 = (base64String + padding)
.replaceAll('-', '+')
.replaceAll('_', '/')
const rawData = window.atob(base64)
return Uint8Array.from([...rawData].map((char) => char.codePointAt(0)))
@ -26,10 +28,8 @@ function getOrCreateServiceWorker() {
}
function subscribePush(registration, isEnabled, vapidPublicKey) {
if (!isEnabled)
return Promise.reject(new Error('Web Push is disabled in config'))
if (!vapidPublicKey)
return Promise.reject(new Error('VAPID public key is not found'))
if (!isEnabled) throw new Error('Web Push is disabled in config')
if (!vapidPublicKey) throw new Error('VAPID public key is not found')
const subscribeOptions = {
userVisibleOnly: false,
@ -38,10 +38,10 @@ function subscribePush(registration, isEnabled, vapidPublicKey) {
return registration.pushManager.subscribe(subscribeOptions)
}
function unsubscribePush(registration) {
async function unsubscribePush(registration) {
return registration.pushManager.getSubscription().then((subscription) => {
if (subscription === null) {
return Promise.resolve('No subscription')
return 'No subscription'
}
return subscription.unsubscribe()
})

View file

@ -165,7 +165,7 @@ export const getCssRules = (rules, debug) =>
header,
directives,
rule.component === 'Text' &&
rule.state.indexOf('faint') < 0 &&
!rule.state.includes('faint') &&
rule.directives.textNoCssColor !== 'yes'
? ' color: var(--text);'
: '',

View file

@ -24,7 +24,7 @@ export const getAllPossibleCombinations = (array) => {
const nonSelf = array.filter((x) => !selfSet.has(x))
return nonSelf.map((x) => [...self, x])
})
const flatCombos = newCombos.reduce((acc, x) => [...acc, ...x], [])
const flatCombos = newCombos.flat()
const uniqueComboStrings = new Set()
const uniqueCombos = flatCombos.map(sortBy).filter((x) => {
if (uniqueComboStrings.has(x.join())) {
@ -36,7 +36,7 @@ export const getAllPossibleCombinations = (array) => {
})
combos.push(uniqueCombos)
}
return combos.reduce((acc, x) => [...acc, ...x], [])
return combos.flat()
}
/**
@ -110,13 +110,6 @@ export const genericRuleToSelector =
let arraySelector = Array.isArray(selector) ? selector : [selector]
if (ignoreOutOfTreeSelector || liteMode)
arraySelector = [arraySelector[0]]
arraySelector
.sort((a) => {
if (a.startsWith(':')) return 1
if (/^[a-z]/.exec(a)) return -1
else return 0
})
.join('')
return arraySelector
})

View file

@ -191,14 +191,16 @@ export const convertTheme2To3 = (data) => {
newRules.push(rule)
if (rule.component === 'Button') {
newRules.push({ ...rule, component: 'ScrollbarElement' })
newRules.push({ ...rule, component: 'Tab' })
newRules.push({
...rule,
component: 'Tab',
state: ['active'],
directives: { opacity: 0 },
})
newRules.push(
{ ...rule, component: 'ScrollbarElement' },
{ ...rule, component: 'Tab' },
{
...rule,
component: 'Tab',
state: ['active'],
directives: { opacity: 0 },
},
)
}
if (rule.component === 'Panel') {
newRules.push({ ...rule, component: 'Post' })
@ -250,8 +252,10 @@ export const convertTheme2To3 = (data) => {
}
newRules.push(rule)
if (rule.component === 'Button') {
newRules.push({ ...rule, component: 'ScrollbarElement' })
newRules.push({ ...rule, component: 'Tab' })
newRules.push(
{ ...rule, component: 'ScrollbarElement' },
{ ...rule, component: 'Tab' },
)
}
})
return newRules
@ -349,16 +353,20 @@ export const convertTheme2To3 = (data) => {
newRules.push({ ...rule, parent: { component: 'Notification' } })
}
if (key === 'buttonPressed') {
newRules.push({ ...rule, state: ['toggled'] })
newRules.push({ ...rule, state: ['toggled', 'focus'] })
newRules.push({ ...rule, state: ['pressed', 'focus'] })
newRules.push({ ...rule, state: ['toggled', 'focus', 'hover'] })
newRules.push({ ...rule, state: ['pressed', 'focus', 'hover'] })
newRules.push(
{ ...rule, state: ['toggled'] },
{ ...rule, state: ['toggled', 'focus'] },
{ ...rule, state: ['pressed', 'focus'] },
{ ...rule, state: ['toggled', 'focus', 'hover'] },
{ ...rule, state: ['pressed', 'focus', 'hover'] },
)
}
if (rule.component === 'Button') {
newRules.push({ ...rule, component: 'ScrollbarElement' })
newRules.push({ ...rule, component: 'Tab' })
newRules.push(
{ ...rule, component: 'ScrollbarElement' },
{ ...rule, component: 'Tab' },
)
}
})
return newRules
@ -512,15 +520,17 @@ export const convertTheme2To3 = (data) => {
{ ...newRule, component: 'Tab' },
{ ...newRule, component: 'ScrollbarElement' },
]
if (newRule.state?.indexOf('toggled') >= 0) {
rules.push({ ...newRule, state: [...newRule.state, 'focused'] })
rules.push({ ...newRule, state: [...newRule.state, 'hover'] })
rules.push({
...newRule,
state: [...newRule.state, 'hover', 'focused'],
})
if (newRule.state?.includes('toggled')) {
rules.push(
{ ...newRule, state: [...newRule.state, 'focused'] },
{ ...newRule, state: [...newRule.state, 'hover'] },
{
...newRule,
state: [...newRule.state, 'hover', 'focused'],
},
)
}
if (newRule.state?.indexOf('hover') >= 0) {
if (newRule.state?.includes('hover')) {
rules.push({ ...newRule, state: [...newRule.state, 'focused'] })
}
return rules
@ -559,9 +569,9 @@ export const convertTheme2To3 = (data) => {
const flatExtRules = extendedRules
.filter(Boolean)
.reduce((acc, x) => [...acc, ...x], [])
.flat()
.filter(Boolean)
.reduce((acc, x) => [...acc, ...x], [])
.flat()
return [
generateRoot(),

View file

@ -244,10 +244,7 @@ export const OPACITIES = Object.entries(SLOT_INHERITANCE).reduce((acc, [k]) => {
...acc,
[opacity]: {
defaultValue: DEFAULT_OPACITY[opacity] || 1,
affectedSlots: [
...((acc[opacity] && acc[opacity].affectedSlots) || []),
k,
],
affectedSlots: [...(acc[opacity]?.affectedSlots || []), k],
},
}
} else {
@ -413,7 +410,7 @@ export const getColors = (sourceColors, sourceOpacity) =>
outputColor.a = Number(
opacityOverriden
? sourceOpacity[opacitySlot]
: (OPACITIES[opacitySlot] || {}).defaultValue,
: OPACITIES[opacitySlot]?.defaultValue,
)
}
}
@ -460,7 +457,7 @@ export const generatePreset = (input) => {
return composePreset(
colors,
generateRadii(input),
generateShadows(input, colors.theme.colors, colors.mod),
generateShadows(input, colors.theme.colors),
generateFonts(input),
)
}

View file

@ -262,7 +262,7 @@ export const init = ({
...r,
})),
)
.reduce((acc, arr) => [...acc, ...arr], []),
.flat(),
...inputRuleset,
].map((rule) => {
normalizeCombination(rule)
@ -690,11 +690,11 @@ export const init = ({
.map((combination) => ['normal', ...combination])
.filter((combo) => {
// Optimization: filter out some hard-coded combinations that don't make sense
if (combo.indexOf('disabled') >= 0) {
if (combo.includes('disabled')) {
return !(
combo.indexOf('hover') >= 0 ||
combo.indexOf('focused') >= 0 ||
combo.indexOf('pressed') >= 0
combo.includes('hover') ||
combo.includes('focused') ||
combo.includes('pressed')
)
}
return true
@ -705,13 +705,13 @@ export const init = ({
.map((variant) => {
return stateCombinations.map((state) => ({ variant, state }))
})
.reduce((acc, x) => [...acc, ...x], [])
.flat()
stateVariantCombination.forEach((combination) => {
combination.component = component.name
combination.lazy = component.lazy || parent?.lazy
combination.parent = parent
if (!liteMode && combination.state.indexOf('hover') >= 0) {
if (!liteMode && combination.state.includes('hover')) {
combination.lazy = true
}

View file

@ -47,7 +47,7 @@ const highlightStyle = (prefs) => {
const highlightClass = (user) => {
return (
'USER____' + user.screen_name?.replace(/\./g, '_').replace(/@/g, '_AT_')
'USER____' + user.screen_name?.replaceAll('.', '_').replaceAll('@', '_AT_')
)
}

View file

@ -61,7 +61,7 @@ export const useChatsStore = defineStore('chats', {
addNewChats(chats) {
window.vuex.commit(
'addNewUsers',
chats.map((k) => k.account).filter((k) => k),
chats.map((k) => k.account).filter(Boolean),
)
chats.forEach((updatedChat) => {
@ -69,8 +69,7 @@ export const useChatsStore = defineStore('chats', {
if (chat) {
const isNewMessage =
(chat.lastMessage && chat.lastMessage.id) !==
(updatedChat.lastMessage && updatedChat.lastMessage.id)
chat.lastMessage?.id !== updatedChat.lastMessage?.id
chat.lastMessage = updatedChat.lastMessage
chat.unread = updatedChat.unread
chat.updated_at = updatedChat.updated_at

View file

@ -120,23 +120,19 @@ export const useEmojiStore = defineStore('emoji', {
}, {})
},
standardEmojiList(state) {
return (
SORTED_EMOJI_GROUP_IDS.map((groupId) =>
(this.emoji[groupId] || []).map((k) =>
injectAnnotations(k, this.unicodeEmojiAnnotations),
),
).reduce((a, b) => a.concat(b), []) ?? []
)
return SORTED_EMOJI_GROUP_IDS.map((groupId) =>
(this.emoji[groupId] || []).map((k) =>
injectAnnotations(k, this.unicodeEmojiAnnotations),
),
).flat()
},
standardEmojiGroupList(state) {
return (
SORTED_EMOJI_GROUP_IDS.map((groupId) => ({
id: groupId,
emojis: (this.emoji[groupId] || []).map((k) =>
injectAnnotations(k, this.unicodeEmojiAnnotations),
),
})) ?? []
)
return SORTED_EMOJI_GROUP_IDS.map((groupId) => ({
id: groupId,
emojis: (this.emoji[groupId] || []).map((k) =>
injectAnnotations(k, this.unicodeEmojiAnnotations),
),
}))
},
},
actions: {
@ -146,7 +142,7 @@ export const useEmojiStore = defineStore('emoji', {
async getStaticEmoji() {
try {
// See build/emojis_plugin for more details
const values = (await import('/src/assets/emoji.json')).default
const values = (await import('src/assets/emoji.json')).default
const emoji = Object.keys(values).reduce((res, groupId) => {
res[groupId] = values[groupId].map((e) => ({
@ -231,7 +227,7 @@ export const useEmojiStore = defineStore('emoji', {
.then((allPacks) => {
// Sort by key
return Object.keys(allPacks)
.sort()
.sort((a, b) => a.localeCompare(b))
.reduce((acc, key) => {
if (key.length === 0) return acc
acc[key] = allPacks[key]

View file

@ -60,8 +60,7 @@ export const useInterfaceStore = defineStore('interface', {
},
browserSupport: {
cssFilter:
window.CSS &&
window.CSS.supports &&
window.CSS?.supports &&
(window.CSS.supports('filter', 'drop-shadow(0 0)') ||
window.CSS.supports('-webkit-filter', 'drop-shadow(0 0)')),
localFonts: typeof window.queryLocalFonts === 'function',
@ -295,7 +294,7 @@ export const useInterfaceStore = defineStore('interface', {
path: 'palettesIndex',
value: { _error: e },
})
return Promise.resolve({})
return {}
}
},
setPalette(value) {
@ -333,7 +332,7 @@ export const useInterfaceStore = defineStore('interface', {
path: 'simple.stylesIndex',
value: { _error: e },
})
return Promise.resolve({})
return {}
}
},
setStyle(value) {
@ -376,7 +375,7 @@ export const useInterfaceStore = defineStore('interface', {
path: 'themesIndex',
value: { _error: e },
})
return Promise.resolve({})
return {}
}
},
setTheme(value) {
@ -578,7 +577,8 @@ export const useInterfaceStore = defineStore('interface', {
return { name: x.variant, ...cleanDirectives }
})
.forEach((palette) => {
const key = 'style.' + palette.name.toLowerCase().replace(/ /g, '_')
const key =
'style.' + palette.name.toLowerCase().replaceAll(' ', '_')
if (!firstStylePaletteName) firstStylePaletteName = key
palettesIndex[key] = () => Promise.resolve(palette)
})

View file

@ -104,7 +104,7 @@ const _verifyPrefs = (state) => {
// Simple
Object.entries(defaultState.prefsStorage.simple).forEach(([k, v]) => {
if (typeof v === 'undefined') return
if (v === undefined) return
if (typeof v === 'number' || typeof v === 'boolean') return
if (typeof v === 'object') return
console.warn(
@ -195,10 +195,13 @@ export const _getRecentData = (cache, live, isTest) => {
}
export const _getAllFlags = (recent, stale) => {
const recentStorage = toRaw(recent?.flagStorage)
const staleStorage = toRaw(stale?.flagStorage)
return Array.from(
new Set([
...Object.keys(toRaw((recent || {}).flagStorage || {})),
...Object.keys(toRaw((stale || {}).flagStorage || {})),
...Object.keys(recentStorage || {}),
...Object.keys(staleStorage || {}),
]),
)
}
@ -833,7 +836,7 @@ export const useSyncConfigStore = defineStore('sync_config', {
: [path, finalValue]
})
newState.prefsStorage.simple = Object.fromEntries(
newEntries.filter((_) => _),
newEntries.filter(Boolean),
)
return newState
},

View file

@ -43,7 +43,7 @@ const _verifyHighlights = (state) => {
// Simple
Object.entries(defaultState.highlight).forEach(([k, v]) => {
if (typeof v === 'undefined') return
if (v === undefined) return
if (typeof v === 'object') return
console.warn(`User highlight ${k} is invalid type ${typeof v}, unsetting`)
delete state.highlight[k]

View file

@ -10,10 +10,10 @@ const server = require('../../build/dev-server.js')
// For more information on Nightwatch's config file, see
// http://nightwatchjs.org/guide#settings-file
let opts = process.argv.slice(2)
if (opts.indexOf('--config') === -1) {
if (!opts.includes('--config')) {
opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js'])
}
if (opts.indexOf('--env') === -1) {
if (!opts.includes('--env')) {
opts = opts.concat(['--env', 'chrome'])
}

View file

@ -66,10 +66,10 @@ describe('ChatView methods', () => {
it("Doesn't add duplicates", () => {
component.vm.addMessages({ messages: [message1] })
component.vm.addMessages({ messages: [message1] })
expect(component.vm.messages.length).to.eql(1)
expect(component.vm.messages).to.have.length(1)
component.vm.addMessages({ messages: [message2] })
expect(component.vm.messages.length).to.eql(2)
expect(component.vm.messages).to.have.length(2)
})
it('Updates minId and lastMessage and newMessageCount', async () => {
@ -127,11 +127,11 @@ describe('ChatView methods', () => {
})
}
component.vm.cullOlder()
expect(component.vm.messages.length).to.eql(50)
expect(component.vm.messages).to.have.length(50)
expect(component.vm.messages[0].id).to.eql('a0.051')
expect(component.vm.minId).to.eql('a0.051')
expect(component.vm.messages[49].id).to.eql('a0.100')
expect(Object.keys(component.vm.messagesIndex).length).to.eql(50)
expect(Object.keys(component.vm.messagesIndex)).to.have.length(50)
})
})
})

View file

@ -80,8 +80,8 @@ describe('PostStatusForm', () => {
expect(wrapper.vm.refId).to.equal('status-1')
expect(wrapper.vm.quotable).to.equal(true)
expect(wrapper.vm.inReplyToStatusId).to.equal('status-1')
expect(wrapper.vm.newStatus.quote).to.eql(null)
expect(wrapper.vm.newStatus.poll).to.eql(null)
expect(wrapper.vm.newStatus.quote).to.be.null
expect(wrapper.vm.newStatus.poll).to.be.null
expect(wrapper.vm.newStatus.spoilerText).to.eql('')
expect(wrapper.vm.newStatus.mentions).to.eql('@replied')
expect(wrapper.vm.newStatus.status).to.eql('@replied ')
@ -101,8 +101,8 @@ describe('PostStatusForm', () => {
expect(wrapper.vm.statusType).to.equal('reply')
expect(wrapper.vm.isReply).to.equal(true)
expect(wrapper.vm.quotable).to.equal(false)
expect(wrapper.vm.newStatus.quote).to.eql(null)
expect(wrapper.vm.newStatus.poll).to.eql(null)
expect(wrapper.vm.newStatus.quote).to.be.null
expect(wrapper.vm.newStatus.poll).to.be.null
expect(wrapper.vm.newStatus.spoilerText).to.eql('re: subject')
expect(wrapper.vm.newStatus.mentions).to.eql('@replied')
expect(wrapper.vm.newStatus.status).to.eql('@replied ')
@ -114,9 +114,9 @@ describe('PostStatusForm', () => {
expect(wrapper.vm.postingOptions.sensitive).to.eql(false)
expect(wrapper.vm.postingOptions.media).to.eql([])
expect(wrapper.vm.postingOptions.inReplyToStatusId).to.eql('status-2')
expect(wrapper.vm.postingOptions.quoteId).to.eql(null)
expect(wrapper.vm.postingOptions.quoteId).to.be.null
expect(wrapper.vm.postingOptions.contentType).to.eql('text/plain')
expect(wrapper.vm.postingOptions.poll).to.eql(null)
expect(wrapper.vm.postingOptions.poll).to.be.null
})
it('Forces direct mode when replying to a DM, mastodon style subject handling', () => {
@ -139,8 +139,8 @@ describe('PostStatusForm', () => {
expect(wrapper.vm.statusType).to.equal('reply')
expect(wrapper.vm.isReply).to.equal(true)
expect(wrapper.vm.quotable).to.equal(false)
expect(wrapper.vm.newStatus.quote).to.eql(null)
expect(wrapper.vm.newStatus.poll).to.eql(null)
expect(wrapper.vm.newStatus.quote).to.be.null
expect(wrapper.vm.newStatus.poll).to.be.null
expect(wrapper.vm.newStatus.spoilerText).to.eql('subject')
expect(wrapper.vm.newStatus.mentions).to.eql('@replied')
expect(wrapper.vm.newStatus.status).to.eql('@replied ')
@ -207,7 +207,7 @@ describe('PostStatusForm', () => {
wrapper.vm.quoteThreadToggled = true
wrapper.vm.quoteThreadToggled = false
expect(wrapper.vm.newStatus.quote).to.eql(null)
expect(wrapper.vm.newStatus.quote).to.be.null
})
it('Initializes and reset quote when toggling quote attachment', () => {
@ -228,7 +228,7 @@ describe('PostStatusForm', () => {
url: '',
})
wrapper.vm.toggleQuoteForm()
expect(wrapper.vm.newStatus.quote).to.eql(null)
expect(wrapper.vm.newStatus.quote).to.be.null
})
it('Status editing', () => {

View file

@ -50,7 +50,7 @@ describe('RichContent', () => {
},
})
expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(html))
expect(wrapper.html().replaceAll('\n', '')).to.eql(compwrap(html))
})
it('unescapes everything as needed', () => {
@ -67,7 +67,7 @@ describe('RichContent', () => {
},
})
expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected))
expect(wrapper.html().replaceAll('\n', '')).to.eql(compwrap(expected))
})
it('replaces mention with mentionsline', () => {
@ -83,7 +83,7 @@ describe('RichContent', () => {
},
})
expect(wrapper.html().replace(/\n/g, '')).to.eql(
expect(wrapper.html().replaceAll('\n', '')).to.eql(
compwrap(p(mentionsLine(1), ' how are you doing today?')),
)
})
@ -116,7 +116,7 @@ describe('RichContent', () => {
},
})
expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected))
expect(wrapper.html().replaceAll('\n', '')).to.eql(compwrap(expected))
})
it('Does not touch links if link handling is disabled', () => {
@ -211,7 +211,7 @@ describe('RichContent', () => {
},
})
expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected))
expect(wrapper.html().replaceAll('\n', '')).to.eql(compwrap(expected))
})
it("Doesn't add nonexistent emoji to post", () => {
@ -228,7 +228,7 @@ describe('RichContent', () => {
},
})
expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(html))
expect(wrapper.html().replaceAll('\n', '')).to.eql(compwrap(html))
})
it('Greentext + last mentions', () => {
@ -279,7 +279,7 @@ describe('RichContent', () => {
},
})
expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected))
expect(wrapper.html().replaceAll('\n', '')).to.eql(compwrap(expected))
})
it('buggy example/hashtags', () => {
@ -315,7 +315,7 @@ describe('RichContent', () => {
},
})
expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected))
expect(wrapper.html().replaceAll('\n', '')).to.eql(compwrap(expected))
})
it('rich contents of a mention are handled properly', () => {
@ -365,8 +365,8 @@ describe('RichContent', () => {
expect(
wrapper
.html()
.replace(/\n/g, '')
.replace(/<!--.*?-->/g, ''),
.replaceAll('\n', '')
.replaceAll(/<!--.*?-->/g, ''),
).to.eql(compwrap(expected))
})
@ -438,8 +438,8 @@ describe('RichContent', () => {
expect(
wrapper
.html()
.replace(/\n/g, '')
.replace(/<!--.*?-->/g, ''),
.replaceAll('\n', '')
.replaceAll(/<!--.*?-->/g, ''),
).to.eql(compwrap(expected))
})
@ -484,7 +484,7 @@ describe('RichContent', () => {
},
})
expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected))
expect(wrapper.html().replaceAll('\n', '')).to.eql(compwrap(expected))
})
it.skip('[INFORMATIVE] Performance testing, 10 000 simple posts', () => {

View file

@ -187,7 +187,7 @@ describe('piniaPersistPlugin', () => {
const test = useTestStore()
test.$patch({ a: 3 })
expect(await mockStorage.getItem('pinia-local-test')).to.eql(undefined)
expect(await mockStorage.getItem('pinia-local-test')).to.be.undefined
// NOTE: it should not even have tried to save, because the subscribe function
// is called only after loading the initial state.
expect(mockStorage.setItem).not.toHaveBeenCalled()

View file

@ -107,7 +107,7 @@ describe('Statuses module', () => {
showImmediately: true,
timeline: 'public',
})
expect(state.timelines.public.maxId).to.eql('1')
expect(state.timelines.public.maxId).to.equal('1')
mutations.addNewStatuses(state, {
statuses: [secondStatus],
@ -120,7 +120,7 @@ describe('Statuses module', () => {
secondStatus,
status,
])
expect(state.timelines.public.maxId).to.eql('1')
expect(state.timelines.public.maxId).to.equal('1')
})
it('keeps a descending by id order in timeline.visibleStatuses and timeline.statuses', () => {
@ -278,7 +278,7 @@ describe('Statuses module', () => {
timeline: 'public',
})
expect(state.timelines.public.visibleStatuses.length).to.eql(1)
expect(state.timelines.public.visibleStatuses).to.have.length(1)
expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(1)
expect(state.timelines.public.maxId).to.eq(favorite.id)
@ -289,7 +289,7 @@ describe('Statuses module', () => {
timeline: 'public',
})
expect(state.timelines.public.visibleStatuses.length).to.eql(1)
expect(state.timelines.public.visibleStatuses).to.have.length(1)
expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(1)
expect(state.timelines.public.maxId).to.eq(favorite.id)
@ -314,7 +314,7 @@ describe('Statuses module', () => {
user,
})
expect(state.timelines.public.visibleStatuses.length).to.eql(1)
expect(state.timelines.public.visibleStatuses).to.have.length(1)
expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(1)
expect(state.timelines.public.visibleStatuses[0].favorited).to.eql(true)
})
@ -340,7 +340,7 @@ describe('Statuses module', () => {
expect(state.allStatusesObject['1'].emoji_reactions[0].me).to.eql(true)
expect(
state.allStatusesObject['1'].emoji_reactions[0].accounts[0].id,
).to.eql('me')
).to.equal('me')
})
it('adds a new reaction', () => {
@ -362,7 +362,7 @@ describe('Statuses module', () => {
expect(state.allStatusesObject['1'].emoji_reactions[0].me).to.eql(true)
expect(
state.allStatusesObject['1'].emoji_reactions[0].accounts[0].id,
).to.eql('me')
).to.equal('me')
})
it('decreases count in existing reaction', () => {
@ -406,7 +406,7 @@ describe('Statuses module', () => {
emoji: '😂',
currentUser: { id: 'me' },
})
expect(state.allStatusesObject['1'].emoji_reactions.length).to.eql(0)
expect(state.allStatusesObject['1'].emoji_reactions).to.have.length(0)
})
})
@ -428,9 +428,9 @@ describe('Statuses module', () => {
state.timelines.public.minId = '5'
mutations.showNewStatuses(state, { timeline: 'public' })
expect(state.timelines.public.visibleStatuses.length).to.eql(2)
expect(state.timelines.public.minVisibleId).to.eql('10')
expect(state.timelines.public.minId).to.eql('10')
expect(state.timelines.public.visibleStatuses).to.have.length(2)
expect(state.timelines.public.minVisibleId).to.equal('10')
expect(state.timelines.public.minId).to.equal('10')
})
})

View file

@ -68,7 +68,7 @@ describe('The users module', () => {
},
}
const name = 'Guy'
expect(getters.findUser(state)(name)).to.eql(undefined)
expect(getters.findUser(state)(name)).to.be.undefined
})
it('returns user with matching id', () => {
@ -114,7 +114,7 @@ describe('The users module', () => {
},
}
const id = '1'
expect(getters.findUserByName(state)(id)).to.eql(undefined)
expect(getters.findUserByName(state)(id)).to.be.undefined
})
})
})

View file

@ -101,7 +101,7 @@ describe('API Entities normalizer', () => {
describe('Mastoapi preprocessing and converting', () => {
it("doesn't blow up", () => {
const parsed = mastoapidata.map(parseStatus)
expect(parsed.length).to.eq(mastoapidata.length)
expect(parsed).to.have.length(mastoapidata.length)
})
it('processes repeats correctly', () => {

View file

@ -125,7 +125,7 @@ describe('The SyncConfig store', () => {
},
})
expect(store.prefsStorage._journal.length).to.eql(500)
expect(store.prefsStorage._journal).to.have.length(500)
})
it('should reset local timestamp to remote if contents are the same', async () => {
@ -173,7 +173,7 @@ describe('The SyncConfig store', () => {
}
store.setPreference({ path: 'simple.palette', value: '1' })
expect(store.prefsStorage.simple.palette).to.eql('1')
expect(store.prefsStorage._journal.length).to.eql(1)
expect(store.prefsStorage._journal).to.have.length(1)
expect(store.prefsStorage._journal[0]).to.eql({
path: 'simple.palette',
operation: 'set',
@ -199,7 +199,7 @@ describe('The SyncConfig store', () => {
store.updateCache({ username: 'test' })
expect(store.prefsStorage.simple.palette).to.eql(2)
expect(store.prefsStorage.collections.palette).to.eql([])
expect(store.prefsStorage._journal.length).to.eql(2)
expect(store.prefsStorage._journal).to.have.length(2)
expect(store.prefsStorage._journal[0]).to.eql({
path: 'simple.palette',
operation: 'set',
@ -229,7 +229,7 @@ describe('The SyncConfig store', () => {
store.updateCache({ username: 'test' })
expect(store.prefsStorage.simple.palette).to.eql(1)
expect(store.prefsStorage.collections.palette).to.eql([2])
expect(store.prefsStorage._journal.length).to.eql(2)
expect(store.prefsStorage._journal).to.have.length(2)
})
// TODO We need a proper test for object-based stores
@ -245,7 +245,7 @@ describe('The SyncConfig store', () => {
expect(store.prefsStorage.simple.fontInput).to.not.have.property(
'family',
)
expect(store.prefsStorage._journal.length).to.eql(1)
expect(store.prefsStorage._journal).to.have.length(1)
})
it('should not allow unsetting depth <= 2', () => {

View file

@ -55,7 +55,7 @@ describe('The UserHighlight store', () => {
user: 'highlight@testing',
type: 'test',
})
expect(store.highlight._journal.length).to.eql(1)
expect(store.highlight._journal).to.have.length(1)
expect(store.highlight._journal[0]).to.eql({
user: 'highlight@testing',
operation: 'set',
@ -74,7 +74,7 @@ describe('The UserHighlight store', () => {
user: 'highlight@testing.xyz',
type: 'test',
})
expect(store.highlight._journal.length).to.eql(1)
expect(store.highlight._journal).to.have.length(1)
expect(store.highlight._journal[0]).to.eql({
user: 'highlight@testing.xyz',
operation: 'set',
@ -98,7 +98,7 @@ describe('The UserHighlight store', () => {
user: 'a@test.xyz',
type: 'foo',
})
expect(store.highlight._journal.length).to.eql(1)
expect(store.highlight._journal).to.have.length(1)
})
})
})

View file

@ -54,7 +54,7 @@ const run = () => {
// Sort by key
const sorted = Object.keys(emojisObject)
.sort()
.sort((a, b) => a.localeCompare(b))
.reduce((acc, key) => {
if (key.length === 0) return acc
acc[key] = emojisObject[key]