diff --git a/README.md b/README.md index 4e879456..2afa6152 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# Pleroma-FE +# Akkoma-FE ![English OK](https://img.shields.io/badge/English-OK-blueviolet) ![日本語OK](https://img.shields.io/badge/%E6%97%A5%E6%9C%AC%E8%AA%9E-OK-blueviolet) -This is a fork of Pleroma-FE from the Pleroma project, with support for new Akkoma features such as: +This is a fork of Akkoma-FE from the Pleroma project, with support for new Akkoma features such as: - MFM support via [marked-mfm](https://akkoma.dev/sfr/marked-mfm) - Custom emoji reactions @@ -21,15 +21,15 @@ This is a fork of Pleroma-FE from the Pleroma project, with support for new Akko # For Translators -The [Weblate UI](https://translate.akkoma.dev/projects/akkoma/pleroma-fe/) is recommended for adding or modifying translations for Pleroma-FE. +The [Weblate UI](https://translate.akkoma.dev/projects/akkoma/pleroma-fe/) is recommended for adding or modifying translations for Akkoma-FE. Alternatively, edit/create `src/i18n/$LANGUAGE_CODE.json` (where `$LANGUAGE_CODE` is the [ISO 639-1 code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) for your language), then add your language to [src/i18n/messages.js](https://akkoma.dev/AkkomaGang/pleroma-fe/src/branch/develop/src/i18n/messages.js) if it doesn't already exist there. -Pleroma-FE will set your language by your browser locale, but you can temporarily force it in the code by changing the locale in main.js. +Akkoma-FE will set your language by your browser locale, but you can temporarily force it in the code by changing the locale in main.js. # FOR ADMINS -To use Pleroma-FE in Akkoma, use the [frontend](https://docs.akkoma.dev/stable/administration/CLI_tasks/frontend/) CLI task to install Pleroma-FE, then modify your configuration as described in the [Frontend Management](https://docs.akkoma.dev/stable/configuration/frontend_management/) doc. +To use Akkoma-FE in Akkoma, use the [frontend](https://docs.akkoma.dev/stable/administration/CLI_tasks/frontend/) CLI task to install Akkoma-FE, then modify your configuration as described in the [Frontend Management](https://docs.akkoma.dev/stable/configuration/frontend_management/) doc. ## Build Setup @@ -65,4 +65,4 @@ Edit config.json for configuration. ### Login methods -```loginMethod``` can be set to either ```password``` (the default) or ```token```, which will use the full oauth redirection flow, which is useful for SSO situations. +```loginMethod``` can be set to either ```password``` (the default) or ```token```, which will use the full oauth redirection flow, which is useful for SSO situations. diff --git a/index.html b/index.html index b4e02603..e2641312 100644 --- a/index.html +++ b/index.html @@ -10,6 +10,7 @@ + diff --git a/package.json b/package.json index e712cffd..4bb94fcd 100644 --- a/package.json +++ b/package.json @@ -18,19 +18,21 @@ "dependencies": { "@babel/runtime": "7.17.8", "@chenfengyuan/vue-qrcode": "2.0.0", + "@floatingghost/pinch-zoom-element": "^1.3.1", "@fortawesome/fontawesome-svg-core": "1.3.0", "@fortawesome/free-regular-svg-icons": "^6.1.2", "@fortawesome/free-solid-svg-icons": "^6.2.0", "@fortawesome/vue-fontawesome": "3.0.1", - "@kazvmoe-infra/pinch-zoom-element": "1.2.0", "@vuelidate/core": "^2.0.0", "@vuelidate/validators": "^2.0.0", + "blurhash": "^2.0.4", "body-scroll-lock": "2.7.1", "chromatism": "3.0.0", "click-outside-vue3": "4.0.1", "cropperjs": "1.5.12", "diff": "3.5.0", "escape-html": "1.0.3", + "iso-639-1": "^2.1.15", "js-cookie": "^3.0.1", "localforage": "1.10.0", "parse-link-header": "^2.0.0", @@ -82,7 +84,6 @@ "html-webpack-plugin": "^5.5.0", "http-proxy-middleware": "0.21.0", "inject-loader": "2.0.1", - "iso-639-1": "2.1.15", "isparta-loader": "2.0.0", "json-loader": "0.5.7", "karma": "6.3.17", diff --git a/src/App.scss b/src/App.scss index 7e6d0dfc..38574cab 100644 --- a/src/App.scss +++ b/src/App.scss @@ -1,6 +1,7 @@ // stylelint-disable rscss/class-format @import './_variables.scss'; - +@import '@fortawesome/fontawesome-svg-core/styles.css'; +@import '@floatingghost/pinch-zoom-element/dist/pinch-zoom.css'; :root { --navbar-height: 3.5rem; --post-line-height: 1.4; diff --git a/src/boot/after_store.js b/src/boot/after_store.js index de261243..0f95237c 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -4,6 +4,8 @@ import { createRouter, createWebHistory } from 'vue-router' import vClickOutside from 'click-outside-vue3' import { FontAwesomeIcon, FontAwesomeLayers } from '@fortawesome/vue-fontawesome' +import { config } from '@fortawesome/fontawesome-svg-core'; +config.autoAddCss = false import App from '../App.vue' import routes from './routes' diff --git a/src/components/attachment/attachment.js b/src/components/attachment/attachment.js index 1a35a77d..7090260e 100644 --- a/src/components/attachment/attachment.js +++ b/src/components/attachment/attachment.js @@ -17,6 +17,7 @@ import { faPencilAlt, faAlignRight } from '@fortawesome/free-solid-svg-icons' +import Blurhash from '../blurhash/Blurhash.vue' library.add( faFile, @@ -61,7 +62,8 @@ const Attachment = { }, components: { StillImage, - VideoAttachment + VideoAttachment, + Blurhash }, computed: { classNames () { @@ -82,6 +84,9 @@ const Attachment = { useContainFit () { return this.$store.getters.mergedConfig.useContainFit }, + useBlurhash () { + return this.$store.getters.mergedConfig.useBlurhash + }, placeholderName () { if (this.attachment.description === '' || !this.attachment.description) { return this.type.toUpperCase() diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue index 947b1bfc..0ccdb776 100644 --- a/src/components/attachment/attachment.vue +++ b/src/components/attachment/attachment.vue @@ -64,7 +64,15 @@ :title="attachment.description" @click.prevent.stop="toggleHidden" > + + + + + + + diff --git a/src/components/desktop_nav/desktop_nav.js b/src/components/desktop_nav/desktop_nav.js index 2fb8a5ac..f4900c38 100644 --- a/src/components/desktop_nav/desktop_nav.js +++ b/src/components/desktop_nav/desktop_nav.js @@ -98,15 +98,11 @@ export default { logoLeft () { return this.$store.state.instance.logoLeft }, currentUser () { return this.$store.state.users.currentUser }, privateMode () { return this.$store.state.instance.private }, - federating () { return this.$store.state.instance.federating }, shouldConfirmLogout () { return this.$store.getters.mergedConfig.modalOnLogout }, showBubbleTimeline () { return this.$store.state.instance.localBubbleInstances.length > 0 - }, - restrictedTimelines () { - return this.$store.state.instance.restrict_unauthenticated.timelines } }, methods: { diff --git a/src/components/desktop_nav/desktop_nav.vue b/src/components/desktop_nav/desktop_nav.vue index a52989a5..92d3fa5b 100644 --- a/src/components/desktop_nav/desktop_nav.vue +++ b/src/components/desktop_nav/desktop_nav.vue @@ -44,7 +44,6 @@ /> @@ -68,7 +67,6 @@ /> diff --git a/src/components/emoji_input/emoji_input.vue b/src/components/emoji_input/emoji_input.vue index 078253c2..d4760edc 100644 --- a/src/components/emoji_input/emoji_input.vue +++ b/src/components/emoji_input/emoji_input.vue @@ -18,6 +18,7 @@ .Status > .status-container > .post-status-form > form > .form-group > .emoji-input > .emoji-picker { + max-width: 100%; + left: 0; + @media (min-width: 1300px) { + left: -30px; + } +} + .Notification { .emoji-picker { min-width: 160%; @@ -7,7 +18,7 @@ overflow: hidden; left: -70%; max-width: 100%; - @media (min-width: 800px) and (max-width: 1300px) { + @media (min-width: 800px) and (max-width: 1280px) { left: -50%; min-width: 50%; max-width: 130%; @@ -18,6 +29,10 @@ min-width: 50%; max-width: 130%; } + + .Status > .emoji-picker { + z-index: 1000; + } } } .emoji-picker { diff --git a/src/components/emoji_picker/emoji_picker.vue b/src/components/emoji_picker/emoji_picker.vue index 00ffb9d2..3ec694f0 100644 --- a/src/components/emoji_picker/emoji_picker.vue +++ b/src/components/emoji_picker/emoji_picker.vue @@ -84,7 +84,10 @@ -
+
{{ $t('emoji.keep_open') }} diff --git a/src/components/extra_buttons/extra_buttons.js b/src/components/extra_buttons/extra_buttons.js index 4bc6144c..5eb98264 100644 --- a/src/components/extra_buttons/extra_buttons.js +++ b/src/components/extra_buttons/extra_buttons.js @@ -144,6 +144,7 @@ const ExtraButtons = { statusPoll: this.status.poll, statusFiles: [...this.status.attachments], statusScope: this.status.visibility, + statusLanguage: this.status.language, statusContentType: data.content_type })) this.doDeleteStatus() diff --git a/src/components/followed_tag_card/FollowedTagCard.vue b/src/components/followed_tag_card/FollowedTagCard.vue new file mode 100644 index 00000000..d9394ddc --- /dev/null +++ b/src/components/followed_tag_card/FollowedTagCard.vue @@ -0,0 +1,77 @@ + + + + + diff --git a/src/components/pinch_zoom/pinch_zoom.js b/src/components/pinch_zoom/pinch_zoom.js index 82670ddf..b7e8f673 100644 --- a/src/components/pinch_zoom/pinch_zoom.js +++ b/src/components/pinch_zoom/pinch_zoom.js @@ -1,4 +1,4 @@ -import PinchZoom from '@kazvmoe-infra/pinch-zoom-element' +import PinchZoom from '@floatingghost/pinch-zoom-element' export default { methods: { diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index d7788414..b8539395 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -13,6 +13,7 @@ import suggestor from '../emoji_input/suggestor.js' import { mapGetters, mapState } from 'vuex' import Checkbox from '../checkbox/checkbox.vue' import Select from '../select/select.vue' +import iso6391 from 'iso-639-1' import { library } from '@fortawesome/fontawesome-svg-core' import { @@ -63,6 +64,7 @@ const PostStatusForm = { 'statusMediaDescriptions', 'statusScope', 'statusContentType', + 'statusLanguage', 'replyTo', 'quoteId', 'repliedUser', @@ -128,7 +130,7 @@ const PostStatusForm = { statusText = buildMentionsString({ user: this.repliedUser, attentions: this.attentions }, currentUser) } - const { postContentType: contentType, sensitiveByDefault, sensitiveIfSubject } = this.$store.getters.mergedConfig + const { postContentType: contentType, sensitiveByDefault, sensitiveIfSubject, interfaceLanguage } = this.$store.getters.mergedConfig let statusParams = { spoilerText: this.subject || '', @@ -139,6 +141,7 @@ const PostStatusForm = { poll: {}, mediaDescriptions: {}, visibility: this.suggestedVisibility(), + language: interfaceLanguage, contentType } @@ -153,6 +156,7 @@ const PostStatusForm = { poll: this.statusPoll || {}, mediaDescriptions: this.statusMediaDescriptions || {}, visibility: this.statusScope || this.suggestedVisibility(), + language: this.statusLanguage || interfaceLanguage, contentType: statusContentType } } @@ -259,7 +263,10 @@ const PostStatusForm = { ...mapGetters(['mergedConfig']), ...mapState({ mobileLayout: state => state.interface.mobileLayout - }) + }), + isoLanguages () { + return iso6391.getAllCodes(); + } }, watch: { 'newStatus': { @@ -282,6 +289,7 @@ const PostStatusForm = { files: [], visibility: newStatus.visibility, contentType: newStatus.contentType, + language: newStatus.language, poll: {}, mediaDescriptions: {} } @@ -341,6 +349,7 @@ const PostStatusForm = { inReplyToStatusId: this.replyTo, quoteId: this.quoteId, contentType: newStatus.contentType, + language: newStatus.language, poll, idempotencyKey: this.idempotencyKey } @@ -375,6 +384,7 @@ const PostStatusForm = { inReplyToStatusId: this.replyTo, quoteId: this.quoteId, contentType: newStatus.contentType, + language: newStatus.language, poll: {}, preview: true }).then((data) => { diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index 9a051262..c2f8917f 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -194,6 +194,23 @@ :on-scope-change="changeVis" /> +
+ +
+
  • + + {{ $t('settings.use_blurhash') }} + +
  • state.users.currentUser, privateMode: state => state.instance.private, federating: state => state.instance.federating, - showBubbleTimeline: state => (state.instance.localBubbleInstances.length > 0), - restrictedTimelines: state => state.instance.restrict_unauthenticated.timelines + showBubbleTimeline: state => (state.instance.localBubbleInstances.length > 0) }) } } diff --git a/src/components/timeline_menu/timeline_menu_content.vue b/src/components/timeline_menu/timeline_menu_content.vue index 220b0278..27aece22 100644 --- a/src/components/timeline_menu/timeline_menu_content.vue +++ b/src/components/timeline_menu/timeline_menu_content.vue @@ -32,7 +32,7 @@ >{{ $t("nav.bubble_timeline") }}
  • -
  • +
  • {{ $t("nav.public_tl") }}
  • -
  • +
  • $store.dispatch('fetchFollowedTags', props.userId), + select: (props, $store) => get($store.getters.findUser(props.userId), 'followedTagIds', []).map(id => $store.getters.findTag(id)), + destroy: (props, $store) => $store.dispatch('clearFollowedTags', props.userId), + childPropName: 'items', + additionalPropNames: ['userId'] +})(List) + const isUserPage = ({ name }) => name === 'user-profile' || name === 'external-user-profile' const UserProfile = { @@ -41,6 +52,7 @@ const UserProfile = { error: false, userId: null, tab: 'statuses', + followsTab: 'users', footerRef: null, note: null, noteLoading: false @@ -165,6 +177,9 @@ const UserProfile = { this.tab = tab this.$router.replace({ hash: `#${tab}` }) }, + onFollowsTabSwitch (tab) { + this.followsTab = tab + }, linkClicked ({ target }) { if (target.tagName === 'SPAN') { target = target.parentNode @@ -200,6 +215,7 @@ const UserProfile = { } }, components: { + FollowedTagCard, UserCard, Timeline, FollowerList, @@ -207,7 +223,8 @@ const UserProfile = { FollowCard, TabSwitcher, Conversation, - RichContent + RichContent, + FollowedTagList, } } diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue index d16483e2..5465778a 100644 --- a/src/components/user_profile/user_profile.vue +++ b/src/components/user_profile/user_profile.vue @@ -37,6 +37,15 @@ :html="field.value" :emoji="user.emoji" /> +
  • @@ -95,22 +104,48 @@ v-if="followsTabVisible" key="followees" :label="$t('user_card.followees')" - :disabled="!user.friends_count" > - - - + +
    + + + +
    +
    + + + + +
    +
    -