diff --git a/src/App.js b/src/App.js index d4b3b41a..10934010 100644 --- a/src/App.js +++ b/src/App.js @@ -3,6 +3,7 @@ import NavPanel from './components/nav_panel/nav_panel.vue' import InstanceSpecificPanel from './components/instance_specific_panel/instance_specific_panel.vue' import FeaturesPanel from './components/features_panel/features_panel.vue' import WhoToFollowPanel from './components/who_to_follow_panel/who_to_follow_panel.vue' +import ShoutPanel from './components/shout_panel/shout_panel.vue' import SettingsModal from './components/settings_modal/settings_modal.vue' import MediaModal from './components/media_modal/media_modal.vue' import ModModal from './components/mod_modal/mod_modal.vue' @@ -28,6 +29,7 @@ export default { InstanceSpecificPanel, FeaturesPanel, WhoToFollowPanel, + ShoutPanel, MediaModal, SideDrawer, MobilePostStatusButton, @@ -79,17 +81,28 @@ export default { } } }, + shout () { return this.$store.state.shout.joined }, suggestionsEnabled () { return this.$store.state.instance.suggestionsEnabled }, showInstanceSpecificPanel () { return this.$store.state.instance.showInstanceSpecificPanel && !this.$store.getters.mergedConfig.hideISP && this.$store.state.instance.instanceSpecificPanelContent }, + isChats () { + return this.$route.name === 'chat' || this.$route.name === 'chats' + }, newPostButtonShown () { + if (this.isChats) return false return this.$store.getters.mergedConfig.alwaysShowNewPostButton || this.layoutType === 'mobile' }, showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel }, editingAvailable () { return this.$store.state.instance.editingAvailable }, + shoutboxPosition () { + return this.$store.getters.mergedConfig.alwaysShowNewPostButton || false + }, + hideShoutbox () { + return this.$store.getters.mergedConfig.hideShoutbox + }, layoutType () { return this.$store.state.interface.layoutType }, privateMode () { return this.$store.state.instance.private }, reverseLayout () { diff --git a/src/App.vue b/src/App.vue index 80ebb525..fb6adca9 100644 --- a/src/App.vue +++ b/src/App.vue @@ -33,7 +33,7 @@
+ diff --git a/src/_variables.scss b/src/_variables.scss index 65ce4027..099d3606 100644 --- a/src/_variables.scss +++ b/src/_variables.scss @@ -27,6 +27,7 @@ $fallback--tooltipRadius: 5px; $fallback--avatarRadius: 4px; $fallback--avatarAltRadius: 10px; $fallback--attachmentRadius: 10px; +$fallback--chatMessageRadius: 10px; $fallback--buttonShadow: 0px 0px 2px 0px rgba(0, 0, 0, 1), 0px 1px 0px 0px rgba(255, 255, 255, 0.2) inset, 0px -1px 0px 0px rgba(0, 0, 0, 0.2) inset; diff --git a/src/boot/after_store.js b/src/boot/after_store.js index de261243..f54537d2 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -274,6 +274,9 @@ const getNodeInfo = async ({ store }) => { store.dispatch('setInstanceOption', { name: 'registrationOpen', value: data.openRegistrations }) store.dispatch('setInstanceOption', { name: 'mediaProxyAvailable', value: features.includes('media_proxy') }) store.dispatch('setInstanceOption', { name: 'safeDM', value: features.includes('safe_dm_mentions') }) + store.dispatch('setInstanceOption', { name: 'shoutAvailable', value: features.includes('chat') }) + store.dispatch('setInstanceOption', { name: 'pleromaChatMessagesAvailable', value: features.includes('pleroma_chat_messages') }) + store.dispatch('setInstanceOption', { name: 'gopherAvailable', value: features.includes('gopher') }) store.dispatch('setInstanceOption', { name: 'pollsAvailable', value: features.includes('polls') }) store.dispatch('setInstanceOption', { name: 'editingAvailable', value: features.includes('editing') }) store.dispatch('setInstanceOption', { name: 'pollLimits', value: metadata.pollLimits }) diff --git a/src/boot/routes.js b/src/boot/routes.js index 93a94a9b..347230d8 100644 --- a/src/boot/routes.js +++ b/src/boot/routes.js @@ -7,6 +7,8 @@ import BookmarkTimeline from 'components/bookmark_timeline/bookmark_timeline.vue import ConversationPage from 'components/conversation-page/conversation-page.vue' import Interactions from 'components/interactions/interactions.vue' import DMs from 'components/dm_timeline/dm_timeline.vue' +import ChatList from 'components/chat_list/chat_list.vue' +import Chat from 'components/chat/chat.vue' import UserProfile from 'components/user_profile/user_profile.vue' import Search from 'components/search/search.vue' import Registration from 'components/registration/registration.vue' @@ -15,6 +17,7 @@ import FollowRequests from 'components/follow_requests/follow_requests.vue' import OAuthCallback from 'components/oauth_callback/oauth_callback.vue' import Notifications from 'components/notifications/notifications.vue' import AuthForm from 'components/auth_form/auth_form.js' +import ShoutPanel from 'components/shout_panel/shout_panel.vue' import WhoToFollow from 'components/who_to_follow/who_to_follow.vue' import About from 'components/about/about.vue' import RemoteUserResolver from 'components/remote_user_resolver/remote_user_resolver.vue' @@ -71,6 +74,7 @@ export default (store) => { { name: 'friend-requests', path: '/friend-requests', component: FollowRequests, beforeEnter: validateAuthenticatedRoute }, { name: 'notifications', path: '/:username/notifications', component: Notifications, props: () => ({ disableTeleport: true }), beforeEnter: validateAuthenticatedRoute }, { name: 'login', path: '/login', component: AuthForm }, + { name: 'shout-panel', path: '/shout-panel', component: ShoutPanel, props: () => ({ floating: false }) }, { name: 'oauth-callback', path: '/oauth-callback', component: OAuthCallback, props: (route) => ({ code: route.query.code }) }, { name: 'search', path: '/search', component: Search, props: (route) => ({ query: route.query.query }) }, { name: 'who-to-follow', path: '/who-to-follow', component: WhoToFollow, beforeEnter: validateAuthenticatedRoute }, @@ -82,5 +86,12 @@ export default (store) => { { name: 'user-profile', path: '/:_(users)?/:name', component: UserProfile, meta: { dontScroll: true } } ] + if (store.state.instance.pleromaChatMessagesAvailable) { + routes = routes.concat([ + { name: 'chat', path: '/users/:username/chats/:recipient_id', component: Chat, meta: { dontScroll: false }, beforeEnter: validateAuthenticatedRoute }, + { name: 'chats', path: '/users/:username/chats', component: ChatList, meta: { dontScroll: false }, beforeEnter: validateAuthenticatedRoute } + ]) + } + return routes } diff --git a/src/components/account_actions/account_actions.js b/src/components/account_actions/account_actions.js index 0f348474..bbab5af0 100644 --- a/src/components/account_actions/account_actions.js +++ b/src/components/account_actions/account_actions.js @@ -1,8 +1,8 @@ +import { mapState } from 'vuex' import ProgressButton from '../progress_button/progress_button.vue' import Popover from '../popover/popover.vue' import ConfirmModal from '../confirm_modal/confirm_modal.vue' import { library } from '@fortawesome/fontawesome-svg-core' -import { mapState } from 'vuex' import { faEllipsisV } from '@fortawesome/free-solid-svg-icons' @@ -68,6 +68,12 @@ const AccountActions = { unmuteDomain () { this.$store.dispatch('unmuteDomain', this.user.screen_name.split('@')[1]) .then(() => this.refetchRelationship()) + }, + openChat () { + this.$router.push({ + name: 'chat', + params: { username: this.$store.state.users.currentUser.screen_name, recipient_id: this.user.id } + }) } }, computed: { diff --git a/src/components/account_actions/account_actions.vue b/src/components/account_actions/account_actions.vue index 656fe802..7e3b79a0 100644 --- a/src/components/account_actions/account_actions.vue +++ b/src/components/account_actions/account_actions.vue @@ -69,6 +69,13 @@ > {{ $t('user_card.mute_domain') }} +