Compare commits

..

21 commits

Author SHA1 Message Date
810266795d Merge branch 'develop' of https://akkoma.dev/AkkomaGang/akkoma-fe into akkowtf 2025-01-05 12:22:34 -05:00
0887b9615e Merge remote-tracking branch 'upstream/develop' into akkowtf 2024-10-28 19:29:31 -04:00
a40ace6120 Revert "modify {backend,frontend}CommitUrl to own softforks"
Turns out this can be modified from adminFE.

This reverts commit 3285603bb6.
2024-10-09 19:42:31 -04:00
3285603bb6 modify {backend,frontend}CommitUrl to own softforks 2024-10-07 21:36:33 -04:00
c4eb918006 Merge remote-tracking branch 'oneric/custom-source' into akkowtf 2024-10-07 21:29:04 -04:00
c4301ae802 Allow using custom source URLs
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
2024-10-03 20:10:43 +02:00
7162eea991 Merge remote-tracking branch 'novenary/fuzzy-emojis' into akkowtf 2024-09-20 11:59:34 -04:00
51374828fc Merge remote-tracking branch 'novenary/misc-fixes/2024-09-17' into akkowtf 2024-09-20 11:59:20 -04:00
novenary
c6de61e081 conversation: scrollIntoView when collapsed
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
This helps find the original context when collapsing a thread on the
timeline.
2024-09-20 18:45:00 +03:00
novenary
8f06566ddd post_status_form: inherit language from parent
If I'm replying to a post in Klingon, chances are I'm going to write in
Klingon. This reduces friction for properly marking post language in a
conversation.
2024-09-20 18:45:00 +03:00
novenary
65dbac098e post_status_form: reset all to defaults on clear 2024-09-20 18:44:57 +03:00
novenary
a624164b39 emoji_input: implement fuzzy suggestions
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
This makes it a lot easier to find what you're looking for with fewer
keystrokes.
2024-09-18 17:11:12 +03:00
9d3cf650ac Merge remote-tracking branch 'novenary/misc-fixes/2024-09-17' into akkowtf 2024-09-17 18:07:04 -04:00
novenary
67c0143b1f post_status_form: inherit language from parent
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
If I'm replying to a post in Klingon, chances are I'm going to write in
Klingon. This reduces friction for properly marking post language in a
conversation.
2024-09-17 21:16:02 +03:00
7caba9c97f Merge remote-tracking branch 'novenary/misc-fixes/2024-09-17' into akkowtf 2024-09-17 12:36:04 -04:00
novenary
c2b2f59c03 post_status_form: inherit language from parent
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
If I'm replying to a post in Klingon, chances are I'm going to write in
Klingon. This reduces friction for properly marking post language in a
conversation.
2024-09-17 12:27:56 +03:00
novenary
ae5bab6cf8 post_status_form: reset all to defaults on clear 2024-09-17 10:59:08 +03:00
novenary
7b8429aa24 emoji_input: show more suggestions
5 suggestions is really too little, so increase the limit and make the
list scrollable.
2024-09-17 03:36:27 +03:00
novenary
2765a5c163 emoji_picker: select recents tab by default
This saves a click to get at your most commonly used emoji.
2024-09-17 00:51:17 +03:00
novenary
5886765d89 post_status_form: fix enter key in subject field
This fixes random actions being triggered by the enter key while the
subject field is focused.

When pressing enter, the browser simulates a click on the first "submit"
button it finds in the form.
A submit button is a button without `type="button"` set.
Remediate this by setting the type attribute on all but the "Post"
button.

Additionally, inhibit the enter key in the subject field (ctrl+enter
still works).
2024-09-17 00:40:34 +03:00
novenary
13383982bc post_status_form: enable sync flush for watcher
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
This fixes drafts not clearing after posting a reply.

Vue 3.3.11 changed watchers to stop firing after component unmount.
After posting a reply, the post form is removed, now causing the queued
event to be discarded.
Synchronous flush causes the handler to be called immediately when
changes happen, solving the problem.

See: https://github.com/vuejs/core/pull/7181
See: 80e2128d52
Fixes: a7dea2f70f
Fixes: #413
2024-09-16 22:40:33 +03:00
20 changed files with 267 additions and 222 deletions

View file

@ -32,6 +32,7 @@
"cropperjs": "^1.6.2", "cropperjs": "^1.6.2",
"diff": "^5.2.0", "diff": "^5.2.0",
"escape-html": "^1.0.3", "escape-html": "^1.0.3",
"fzy.js": "^0.4.1",
"iso-639-1": "^2.1.15", "iso-639-1": "^2.1.15",
"js-cookie": "^3.0.1", "js-cookie": "^3.0.1",
"localforage": "^1.10.0", "localforage": "^1.10.0",

View file

@ -414,6 +414,11 @@ const conversation = {
}, },
toggleExpanded () { toggleExpanded () {
this.expanded = !this.expanded this.expanded = !this.expanded
this.$nextTick(() => {
if (!this.expanded) {
this.$el.scrollIntoView({ block: 'nearest' })
}
})
}, },
getConversationId (statusId) { getConversationId (statusId) {
const status = this.$store.state.statuses.allStatusesObject[statusId] const status = this.$store.state.statuses.allStatusesObject[statusId]

View file

@ -278,5 +278,8 @@
&.-expanded.status-fadein { &.-expanded.status-fadein {
margin: calc(var(--status-margin, $status-margin) / 2); margin: calc(var(--status-margin, $status-margin) / 2);
} }
/* HACK: this value was picked arbitrarily and this is likely not even the right place */
scroll-margin-block-start: calc(var(--navbar-height) * 2);
} }
</style> </style>

View file

@ -183,7 +183,7 @@ const EmojiInput = {
// Async: cancel if textAtCaret has changed during wait // Async: cancel if textAtCaret has changed during wait
if (this.textAtCaret !== newWord) return if (this.textAtCaret !== newWord) return
if (matchedSuggestions.length <= 0) return if (matchedSuggestions.length <= 0) return
this.suggestions = take(matchedSuggestions, 5) this.suggestions = take(matchedSuggestions, 25)
.map(({ imageUrl, ...rest }) => ({ .map(({ imageUrl, ...rest }) => ({
...rest, ...rest,
img: imageUrl || '' img: imageUrl || ''
@ -300,6 +300,9 @@ const EmojiInput = {
if (this.highlighted < 0) { if (this.highlighted < 0) {
this.highlighted = this.suggestions.length - 1 this.highlighted = this.suggestions.length - 1
} }
const panelBody = this.$refs['panel-body']
const highlighted = panelBody.children[this.highlighted]
highlighted.scrollIntoView({ block: 'nearest' })
e.preventDefault() e.preventDefault()
} else { } else {
this.highlighted = 0 this.highlighted = 0
@ -312,6 +315,9 @@ const EmojiInput = {
if (this.highlighted >= len) { if (this.highlighted >= len) {
this.highlighted = 0 this.highlighted = 0
} }
const panelBody = this.$refs['panel-body']
const highlighted = panelBody.children[this.highlighted]
highlighted.scrollIntoView({ block: 'nearest' })
e.preventDefault() e.preventDefault()
} else { } else {
this.highlighted = 0 this.highlighted = 0

View file

@ -128,6 +128,12 @@
--postLink: var(--popoverPostLink, $fallback--link); --postLink: var(--popoverPostLink, $fallback--link);
--postFaintLink: var(--popoverPostFaintLink, $fallback--link); --postFaintLink: var(--popoverPostFaintLink, $fallback--link);
--icon: var(--popoverIcon, $fallback--icon); --icon: var(--popoverIcon, $fallback--icon);
overflow-y: scroll;
scrollbar-gutter: stable;
scrollbar-width: thin;
max-height: calc((0.2em * 2 + 1px + 32px) * 5);
scroll-padding-block: calc((0.2em * 2 + 1px + 32px) * 2);
} }
} }

View file

@ -1,3 +1,6 @@
import * as fzy from 'fzy.js'
import { sortBy } from 'lodash'
const MFM_TAGS = ['blur', 'bounce', 'flip', 'font', 'jelly', 'jump', 'rainbow', 'rotate', 'shake', 'sparkle', 'spin', 'tada', 'twitch', 'x2', 'x3', 'x4'] const MFM_TAGS = ['blur', 'bounce', 'flip', 'font', 'jelly', 'jump', 'rainbow', 'rotate', 'shake', 'sparkle', 'spin', 'tada', 'twitch', 'x2', 'x3', 'x4']
.map(tag => ({ displayText: tag, detailText: '$[' + tag + ' ]', replacement: '$[' + tag + ' ]', mfm: true })) .map(tag => ({ displayText: tag, detailText: '$[' + tag + ' ]', replacement: '$[' + tag + ' ]', mfm: true }))
@ -34,6 +37,20 @@ export default data => {
export const suggestEmoji = emojis => input => { export const suggestEmoji = emojis => input => {
const noPrefix = input.toLowerCase().substr(1) const noPrefix = input.toLowerCase().substr(1)
if (true) { // TODO check option setting
const matches = emojis.filter(({ displayText }) => fzy.hasMatch(noPrefix, displayText))
return sortBy(matches, m => {
let score = fzy.score(noPrefix, m.displayText)
// Prioritize custom emoji a lot
score += m.imageUrl ? 100 : 0
// Sort in descending order
return -score
})
}
return emojis return emojis
.filter(({ displayText }) => displayText.toLowerCase().match(noPrefix)) .filter(({ displayText }) => displayText.toLowerCase().match(noPrefix))
.sort((a, b) => { .sort((a, b) => {

View file

@ -31,7 +31,7 @@ const EmojiPicker = {
data () { data () {
return { return {
keyword: '', keyword: '',
activeGroup: 'standard', activeGroup: 'recent',
showingStickers: false, showingStickers: false,
keepOpen: false keepOpen: false
} }

View file

@ -24,6 +24,7 @@
<button <button
v-if="options.length > 2" v-if="options.length > 2"
class="delete-option button-unstyled -hover-highlight" class="delete-option button-unstyled -hover-highlight"
type="button"
@click="deleteOption(index)" @click="deleteOption(index)"
> >
<FAIcon icon="times" /> <FAIcon icon="times" />
@ -32,6 +33,7 @@
<button <button
v-if="options.length < maxOptions" v-if="options.length < maxOptions"
class="add-option faint button-unstyled -hover-highlight" class="add-option faint button-unstyled -hover-highlight"
type="button"
@click="addOption" @click="addOption"
> >
<FAIcon <FAIcon

View file

@ -85,6 +85,7 @@ const PostStatusForm = {
'quoteId', 'quoteId',
'repliedUser', 'repliedUser',
'attentions', 'attentions',
'copyMessageLanguage',
'copyMessageScope', 'copyMessageScope',
'subject', 'subject',
'disableSubject', 'disableSubject',
@ -153,8 +154,7 @@ const PostStatusForm = {
statusText = buildMentionsString({ user: this.repliedUser, attentions: this.attentions }, currentUser) statusText = buildMentionsString({ user: this.repliedUser, attentions: this.attentions }, currentUser)
} }
const { postContentType: contentType, postLanguage: defaultPostLanguage, sensitiveByDefault, sensitiveIfSubject, interfaceLanguage, alwaysShowSubjectInput } = this.$store.getters.mergedConfig const { postContentType: contentType, sensitiveByDefault, sensitiveIfSubject, alwaysShowSubjectInput } = this.$store.getters.mergedConfig
const postLanguage = defaultPostLanguage || interfaceToISOLanguage(interfaceLanguage)
let statusParams = { let statusParams = {
spoilerText: this.subject || '', spoilerText: this.subject || '',
@ -165,7 +165,7 @@ const PostStatusForm = {
poll: {}, poll: {},
mediaDescriptions: {}, mediaDescriptions: {},
visibility: this.suggestedVisibility(), visibility: this.suggestedVisibility(),
language: postLanguage, language: this.suggestedLanguage(),
contentType contentType
} }
@ -180,7 +180,7 @@ const PostStatusForm = {
poll: this.statusPoll || {}, poll: this.statusPoll || {},
mediaDescriptions: this.statusMediaDescriptions || {}, mediaDescriptions: this.statusMediaDescriptions || {},
visibility: this.statusScope || this.suggestedVisibility(), visibility: this.statusScope || this.suggestedVisibility(),
language: this.statusLanguage || postLanguage, language: this.statusLanguage || this.suggestedLanguage(),
contentType: statusContentType contentType: statusContentType
} }
} }
@ -342,17 +342,22 @@ const PostStatusForm = {
this.saveDraft() this.saveDraft()
}, },
clearStatus () { clearStatus () {
const newStatus = this.newStatus const config = this.$store.getters.mergedConfig
this.newStatus = { this.newStatus = {
status: '', status: '',
spoilerText: '', spoilerText: '',
files: [], files: [],
visibility: newStatus.visibility, nsfw: !!config.sensitiveByDefault,
contentType: newStatus.contentType, visibility: this.suggestedVisibility(),
language: newStatus.language, contentType: config.postContentType,
language: this.suggestedLanguage(),
poll: {}, poll: {},
mediaDescriptions: {} mediaDescriptions: {}
} }
const scopeselector = this.$refs.scopeselector
if (scopeselector) {
scopeselector.currentScope = this.newStatus.visibility
}
this.pollFormVisible = false this.pollFormVisible = false
this.$refs.mediaUpload && this.$refs.mediaUpload.clearFile() this.$refs.mediaUpload && this.$refs.mediaUpload.clearFile()
this.clearPollForm() this.clearPollForm()
@ -761,6 +766,15 @@ const PostStatusForm = {
openProfileTab () { openProfileTab () {
this.$store.dispatch('openSettingsModalTab', 'profile') this.$store.dispatch('openSettingsModalTab', 'profile')
}, },
suggestedLanguage () {
// Make sure the inherited language is actually valid
if (this.postLanguageOptions.find(o => o.value === this.copyMessageLanguage)) {
return this.copyMessageLanguage
}
const { postLanguage: defaultPostLanguage, interfaceLanguage } = this.$store.getters.mergedConfig
const postLanguage = defaultPostLanguage || interfaceToISOLanguage(interfaceLanguage)
return postLanguage
},
suggestedVisibility () { suggestedVisibility () {
if (this.copyMessageScope) { if (this.copyMessageScope) {
if (this.copyMessageScope === 'direct') { if (this.copyMessageScope === 'direct') {

View file

@ -18,6 +18,7 @@
> >
<button <button
class="button-unstyled -link" class="button-unstyled -link"
type="button"
@click="openProfileTab" @click="openProfileTab"
> >
{{ $t('post_status.account_not_locked_warning_link') }} {{ $t('post_status.account_not_locked_warning_link') }}
@ -136,6 +137,7 @@
class="form-post-subject" class="form-post-subject"
@input="onSubjectInput" @input="onSubjectInput"
@focus="focusSubjectInput()" @focus="focusSubjectInput()"
@keydown.exact.enter.prevent
> >
</EmojiInput> </EmojiInput>
<i18n-t <i18n-t
@ -193,6 +195,7 @@
:class="{ 'visibility-tray-edit': isEdit }" :class="{ 'visibility-tray-edit': isEdit }"
> >
<scope-selector <scope-selector
ref="scopeselector"
v-if="!disableVisibilitySelector" v-if="!disableVisibilitySelector"
:user-default="userDefaultScope" :user-default="userDefaultScope"
:original-scope="copyMessageScope" :original-scope="copyMessageScope"
@ -272,6 +275,7 @@
<button <button
class="emoji-icon button-unstyled" class="emoji-icon button-unstyled"
:title="$t('emoji.add_emoji')" :title="$t('emoji.add_emoji')"
type="button"
@click="showEmojiPicker" @click="showEmojiPicker"
> >
<FAIcon icon="smile-beam" /> <FAIcon icon="smile-beam" />
@ -281,6 +285,7 @@
class="poll-icon button-unstyled" class="poll-icon button-unstyled"
:class="{ selected: pollFormVisible }" :class="{ selected: pollFormVisible }"
:title="$t('polls.add_poll')" :title="$t('polls.add_poll')"
type="button"
@click="togglePollForm" @click="togglePollForm"
> >
<FAIcon icon="poll-h" /> <FAIcon icon="poll-h" />
@ -290,6 +295,7 @@
class="spoiler-icon button-unstyled" class="spoiler-icon button-unstyled"
:class="{ selected: subjectVisible }" :class="{ selected: subjectVisible }"
:title="$t('post_status.toggle_content_warning')" :title="$t('post_status.toggle_content_warning')"
type="button"
@click="toggleSubjectVisible" @click="toggleSubjectVisible"
> >
<FAIcon icon="eye-slash" /> <FAIcon icon="eye-slash" />

View file

@ -159,16 +159,6 @@
{{ $t('settings.show_page_backgrounds') }} {{ $t('settings.show_page_backgrounds') }}
</BooleanSetting> </BooleanSetting>
</li> </li>
<li>
<BooleanSetting path="centerAlignBio">
{{ $t('settings.center_align_bio') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="compactUserInfo">
{{ $t('settings.compact_user_info') }}
</BooleanSetting>
</li>
<li> <li>
<BooleanSetting path="stopGifs"> <BooleanSetting path="stopGifs">
{{ $t('settings.stop_gifs') }} {{ $t('settings.stop_gifs') }}

View file

@ -519,6 +519,7 @@
:reply-to="status.id" :reply-to="status.id"
:attentions="status.attentions" :attentions="status.attentions"
:replied-user="status.user" :replied-user="status.user"
:copy-message-language="status.language"
:copy-message-scope="status.visibility" :copy-message-scope="status.visibility"
:subject="replySubject" :subject="replySubject"
@posted="toggleReplying" @posted="toggleReplying"
@ -533,6 +534,7 @@
:quote-id="status.id" :quote-id="status.id"
:attentions="[status.user]" :attentions="[status.user]"
:replied-user="status.user" :replied-user="status.user"
:copy-message-language="status.language"
:copy-message-scope="status.visibility" :copy-message-scope="status.visibility"
:subject="replySubject" :subject="replySubject"
@posted="toggleQuoting" @posted="toggleQuoting"

View file

@ -117,11 +117,6 @@ export default {
shouldConfirmMute () { shouldConfirmMute () {
return this.mergedConfig.modalOnMute return this.mergedConfig.modalOnMute
}, },
compactUserInfo () {
return this.$store.getters.mergedConfig.compactUserInfo
&& (this.$store.state.interface.layoutType !== 'mobile')
&& this.switcher
},
...mapGetters(['mergedConfig']) ...mapGetters(['mergedConfig'])
}, },
components: { components: {

View file

@ -21,13 +21,6 @@
position: relative; position: relative;
} }
.user-buttons {
grid-area: edit;
display: flex;
padding: .5em 0 .5em 0;
justify-self: end;
}
.panel-body { .panel-body {
word-wrap: break-word; word-wrap: break-word;
border-bottom-right-radius: inherit; border-bottom-right-radius: inherit;
@ -60,6 +53,7 @@
} }
&-bio { &-bio {
text-align: center;
display: block; display: block;
line-height: 1.3; line-height: 1.3;
padding: 1em; padding: 1em;
@ -106,14 +100,15 @@
padding: 0 26px; padding: 0 26px;
.container { .container {
min-width: 0;
padding: 16px 0 6px; padding: 16px 0 6px;
display: grid; display: flex;
grid-template-areas: align-items: flex-start;
"pfp name edit" max-height: 56px;
"pfp summary summary"
"stats stats stats"; > * {
grid-template-columns: auto 1fr auto; min-width: 0;
align-items: start; }
.Avatar { .Avatar {
--_avatarShadowBox: var(--avatarShadow); --_avatarShadowBox: var(--avatarShadow);
@ -128,7 +123,6 @@
} }
&-avatar-link { &-avatar-link {
grid-area: pfp;
position: relative; position: relative;
cursor: pointer; cursor: pointer;
@ -159,8 +153,8 @@
.external-link-button, .edit-profile-button { .external-link-button, .edit-profile-button {
cursor: pointer; cursor: pointer;
width: 2.3em; width: 2.5em;
text-align: right; text-align: center;
margin: -0.5em 0; margin: -0.5em 0;
padding: 0.5em 0; padding: 0.5em 0;
@ -171,16 +165,12 @@
} }
.user-summary { .user-summary {
grid-area: summary; display: block;
display: grid;
grid-template-areas:
"name name name name name"
"hand role lock avg _";
grid-template-columns:
auto auto auto auto 1fr;
justify-items: start;
margin-left: 0.6em; margin-left: 0.6em;
text-align: left;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap;
flex: 1 1 0;
// This is so that text doesn't get overlapped by avatar's shadow if it has // This is so that text doesn't get overlapped by avatar's shadow if it has
// big one // big one
z-index: 1; z-index: 1;
@ -188,81 +178,55 @@
--emoji-size: 1.7em; --emoji-size: 1.7em;
.user-locked { .top-line,
.bottom-line {
display: flex;
}
}
.user-name {
text-overflow: ellipsis;
overflow: hidden;
flex: 1 1 auto;
margin-right: 1em;
font-size: 1.1em;
}
.bottom-line {
font-weight: light;
font-size: 1.1em;
align-items: baseline;
.lock-icon {
margin-left: 0.5em; margin-left: 0.5em;
grid-area: lock;
} }
.user-screen-name { .user-screen-name {
min-width: 1px; min-width: 1px;
max-width: 100%; flex: 0 1 auto;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
color: $fallback--lightText; color: $fallback--lightText;
color: var(--lightText, $fallback--lightText); color: var(--lightText, $fallback--lightText);
grid-area: hand;
} }
.dailyAvg { .dailyAvg {
min-width: 1px; min-width: 1px;
flex: 0 0 auto;
margin-left: 1em; margin-left: 1em;
font-size: 0.7em; font-size: 0.7em;
color: $fallback--text; color: $fallback--text;
color: var(--text, $fallback--text); color: var(--text, $fallback--text);
grid-area: avg;
} }
.user-roles {
display: flex;
grid-area: role;
.user-role { .user-role {
flex: none;
color: $fallback--text; color: $fallback--text;
color: var(--alertNeutralText, $fallback--text); color: var(--alertNeutralText, $fallback--text);
background-color: $fallback--fg; background-color: $fallback--fg;
background-color: var(--alertNeutral, $fallback--fg); background-color: var(--alertNeutral, $fallback--fg);
} }
} }
}
.user-counts {
grid-area: stats;
display: flex;
line-height:16px;
padding-top: 0.5em;
text-align: center;
justify-content: space-around;
color: $fallback--lightText;
color: var(--lightText, $fallback--lightText);
align-self: center;
.user-count {
padding: .5em 0 .5em 0;
margin: 0 .5em;
h5 {
font-size:1em;
font-weight: bolder;
margin: 0 0 0.25em;
}
a {
text-decoration: none;
}
}
}
.user-name {
text-align: start;
text-overflow: ellipsis;
overflow: hidden;
margin-left: 0.6em;
font-size: 1.1em;
grid-area: name;
align-self: center;
white-space: nowrap;
max-width: 100%;
z-index: 1; // so shadow from user avatar doesn't overlap it
}
.user-meta { .user-meta {
margin-bottom: .15em; margin-bottom: .15em;
@ -326,21 +290,34 @@
margin: 0; margin: 0;
} }
} }
&.-compact {
.container {
grid-template-areas:
"pfp name stats edit"
"pfp summary stats edit";
grid-template-columns: auto auto 1fr auto;
}
.user-counts {
padding-top: 0;
justify-content: space-evenly;
}
}
} }
.sidebar .edit-profile-button { .sidebar .edit-profile-button {
display: none; display: none;
} }
.user-counts {
display: flex;
line-height:16px;
padding: .5em 1.5em 0em 1.5em;
text-align: center;
justify-content: space-between;
color: $fallback--lightText;
color: var(--lightText, $fallback--lightText);
flex-wrap: wrap;
}
.user-count {
flex: 1 0 auto;
padding: .5em 0 .5em 0;
margin: 0 .5em;
h5 {
font-size:1em;
font-weight: bolder;
margin: 0 0 0.25em;
}
a {
text-decoration: none;
}
}

View file

@ -9,10 +9,7 @@
class="background-image" class="background-image"
/> />
<div class="panel-heading -flexible-height"> <div class="panel-heading -flexible-height">
<div <div class="user-info">
class="user-info"
:class="{ '-compact': this.compactUserInfo }"
>
<div class="container"> <div class="container">
<a <a
v-if="allowZoomingAvatar" v-if="allowZoomingAvatar"
@ -32,7 +29,6 @@
</a> </a>
<router-link <router-link
v-else v-else
class="user-info-avatar-link"
:to="userProfileLink(user)" :to="userProfileLink(user)"
> >
<UserAvatar <UserAvatar
@ -40,79 +36,14 @@
:user="user" :user="user"
/> />
</router-link> </router-link>
<div class="user-summary">
<div class="top-line">
<RichContent <RichContent
:title="user.name" :title="user.name"
class="user-name" class="user-name"
:html="user.name" :html="user.name"
:emoji="user.emoji" :emoji="user.emoji"
/> />
<div class="user-summary">
<router-link
class="user-screen-name"
:title="user.screen_name_ui"
:to="userProfileLink(user)"
>
@{{ user.screen_name_ui }}
</router-link>
<span class="user-roles" v-if="!hideBio && (user.deactivated || !!visibleRole || user.bot)">
<span
v-if="user.deactivated"
class="alert user-role"
>
{{ $t('user_card.deactivated') }}
</span>
<span
v-if="!!visibleRole"
class="alert user-role"
>
{{ $t(`general.role.${visibleRole}`) }}
</span>
<span
v-if="user.bot"
class="alert user-role"
>
{{ $t('user_card.bot') }}
</span>
</span>
<span class="user-locked" v-if="user.locked">
<FAIcon
class="lock-icon"
icon="lock"
size="sm"
/>
</span>
<span
v-if="!mergedConfig.hideUserStats && !hideBio"
class="dailyAvg"
>{{ dailyAvg }} {{ $t('user_card.per_day') }}</span>
</div>
<div
v-if="!mergedConfig.hideUserStats && switcher"
class="user-counts"
>
<div
class="user-count"
@click.prevent="setProfileView('statuses')"
>
<h5>{{ $t('user_card.statuses') }}</h5>
<span>{{ user.statuses_count }} <br></span>
</div>
<div
class="user-count"
@click.prevent="setProfileView('friends')"
>
<h5>{{ $t('user_card.followees') }}</h5>
<span>{{ hideFollowsCount ? $t('user_card.hidden') : user.friends_count }}</span>
</div>
<div
class="user-count"
@click.prevent="setProfileView('followers')"
>
<h5>{{ $t('user_card.followers') }}</h5>
<span>{{ hideFollowersCount ? $t('user_card.hidden') : user.followers_count }}</span>
</div>
</div>
<div class="user-buttons">
<button <button
v-if="!isOtherUser && user.is_local" v-if="!isOtherUser && user.is_local"
class="button-unstyled edit-profile-button" class="button-unstyled edit-profile-button"
@ -153,6 +84,47 @@
:relationship="relationship" :relationship="relationship"
/> />
</div> </div>
<div class="bottom-line">
<router-link
class="user-screen-name"
:title="user.screen_name_ui"
:to="userProfileLink(user)"
>
@{{ user.screen_name_ui }}
</router-link>
<template v-if="!hideBio">
<span
v-if="user.deactivated"
class="alert user-role"
>
{{ $t('user_card.deactivated') }}
</span>
<span
v-if="!!visibleRole"
class="alert user-role"
>
{{ $t(`general.role.${visibleRole}`) }}
</span>
<span
v-if="user.bot"
class="alert user-role"
>
{{ $t('user_card.bot') }}
</span>
</template>
<span v-if="user.locked">
<FAIcon
class="lock-icon"
icon="lock"
size="sm"
/>
</span>
<span
v-if="!mergedConfig.hideUserStats && !hideBio"
class="dailyAvg"
>{{ dailyAvg }} {{ $t('user_card.per_day') }}</span>
</div>
</div>
</div> </div>
<div class="user-meta"> <div class="user-meta">
<div <div
@ -297,13 +269,38 @@
v-if="!hideBio" v-if="!hideBio"
class="panel-body" class="panel-body"
> >
<div
v-if="!mergedConfig.hideUserStats && switcher"
class="user-counts"
>
<div
class="user-count"
@click.prevent="setProfileView('statuses')"
>
<h5>{{ $t('user_card.statuses') }}</h5>
<span>{{ user.statuses_count }} <br></span>
</div>
<div
class="user-count"
@click.prevent="setProfileView('friends')"
>
<h5>{{ $t('user_card.followees') }}</h5>
<span>{{ hideFollowsCount ? $t('user_card.hidden') : user.friends_count }}</span>
</div>
<div
class="user-count"
@click.prevent="setProfileView('followers')"
>
<h5>{{ $t('user_card.followers') }}</h5>
<span>{{ hideFollowersCount ? $t('user_card.hidden') : user.followers_count }}</span>
</div>
</div>
<RichContent <RichContent
v-if="!hideBio" v-if="!hideBio"
class="user-card-bio" class="user-card-bio"
:html="user.description_html" :html="user.description_html"
:emoji="user.emoji" :emoji="user.emoji"
:handle-links="true" :handle-links="true"
:style='{"text-align": this.$store.getters.mergedConfig.centerAlignBio ? "center" : "start"}'
/> />
</div> </div>
<teleport to="#modal"> <teleport to="#modal">

View file

@ -482,7 +482,6 @@
"blocks_tab": "Blocks", "blocks_tab": "Blocks",
"bot": "Dies ist ein Bot Account", "bot": "Dies ist ein Bot Account",
"btnRadius": "Knöpfe", "btnRadius": "Knöpfe",
"center_align_bio": "Zentrale Textausrichtung in der Bio",
"cBlue": "Blau (Antworten, folgt dir)", "cBlue": "Blau (Antworten, folgt dir)",
"cGreen": "Grün (Retweet)", "cGreen": "Grün (Retweet)",
"cOrange": "Orange (Favorisieren)", "cOrange": "Orange (Favorisieren)",
@ -497,7 +496,6 @@
"checkboxRadius": "Auswahlfelder", "checkboxRadius": "Auswahlfelder",
"collapse_subject": "Beiträge mit Inhaltswarnungen einklappen", "collapse_subject": "Beiträge mit Inhaltswarnungen einklappen",
"columns": "Spalten", "columns": "Spalten",
"compact_user_info": "Kompakte Benutzerinfos wenn genug Platz",
"composing": "Verfassen", "composing": "Verfassen",
"confirm_dialogs": "Bestätigung erforderlich für:", "confirm_dialogs": "Bestätigung erforderlich für:",
"confirm_dialogs_approve_follow": "Annehmen einer Followanfrage", "confirm_dialogs_approve_follow": "Annehmen einer Followanfrage",

View file

@ -488,7 +488,6 @@
"blocks_tab": "Blocks", "blocks_tab": "Blocks",
"bot": "This is a bot account", "bot": "This is a bot account",
"btnRadius": "Buttons", "btnRadius": "Buttons",
"center_align_bio": "Center text in user bio",
"cBlue": "Blue (Reply, follow)", "cBlue": "Blue (Reply, follow)",
"cGreen": "Green (Retweet)", "cGreen": "Green (Retweet)",
"cOrange": "Orange (Favorite)", "cOrange": "Orange (Favorite)",
@ -503,7 +502,6 @@
"checkboxRadius": "Checkboxes", "checkboxRadius": "Checkboxes",
"collapse_subject": "Collapse posts with content warnings", "collapse_subject": "Collapse posts with content warnings",
"columns": "Columns", "columns": "Columns",
"compact_user_info": "Compact user info when enough space",
"composing": "Composing", "composing": "Composing",
"confirm_dialogs": "Require confirmation for:", "confirm_dialogs": "Require confirmation for:",
"confirm_dialogs_approve_follow": "Accepting a follow request", "confirm_dialogs_approve_follow": "Accepting a follow request",

View file

@ -56,8 +56,6 @@ export const defaultState = {
autohideFloatingPostButton: false, autohideFloatingPostButton: false,
pauseOnUnfocused: true, pauseOnUnfocused: true,
displayPageBackgrounds: true, displayPageBackgrounds: true,
centerAlignBio: false,
compactUserInfo: true,
stopGifs: undefined, stopGifs: undefined,
replyVisibility: 'all', replyVisibility: 'all',
thirdColumnMode: 'notifications', thirdColumnMode: 'notifications',

View file

@ -326,6 +326,7 @@ export const parseStatus = (data) => {
} }
output.pinned = data.pinned output.pinned = data.pinned
output.muted = data.muted output.muted = data.muted
output.language = data.language
} else { } else {
output.favorited = data.favorited output.favorited = data.favorited
output.fave_num = data.fave_num output.fave_num = data.fave_num

View file

@ -4283,6 +4283,11 @@ functions-have-names@^1.2.3:
resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
fzy.js@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/fzy.js/-/fzy.js-0.4.1.tgz#695bf87cc0bbdda9cbcf22bc318a74c4aca6b758"
integrity sha512-4sPVXf+9oGhzg2tYzgWe4hgAY0wEbkqeuKVEgdnqX8S8VcLosQsDjb0jV+f5uoQlf8INWId1w0IGoufAoik1TA==
gensync@^1.0.0-beta.2: gensync@^1.0.0-beta.2:
version "1.0.0-beta.2" version "1.0.0-beta.2"
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
@ -7390,7 +7395,16 @@ streamroller@^3.1.5:
debug "^4.3.4" debug "^4.3.4"
fs-extra "^8.1.0" fs-extra "^8.1.0"
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: "string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
version "4.2.3" version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@ -7477,7 +7491,14 @@ stringify-object@^3.3.0:
is-obj "^1.0.1" is-obj "^1.0.1"
is-regexp "^1.0.0" is-regexp "^1.0.0"
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: "strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1" version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@ -8577,8 +8598,7 @@ workerpool@6.2.1:
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343"
integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
name wrap-ansi-cjs
version "7.0.0" version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@ -8596,6 +8616,15 @@ wrap-ansi@^6.2.0:
string-width "^4.1.0" string-width "^4.1.0"
strip-ansi "^6.0.0" strip-ansi "^6.0.0"
wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrap-ansi@^8.1.0: wrap-ansi@^8.1.0:
version "8.1.0" version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"