Skip to content

Commit 3ad4b7b

Browse files
authored
Merge pull request #749 from Adamant-im/feat/chats-one-screen
Feat/chats one screen
2 parents 0dc3a9f + ab49b44 commit 3ad4b7b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+2680
-2136
lines changed

src/App.vue

Lines changed: 59 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3,67 +3,78 @@
33
<UploadAttachmentExitPrompt />
44
<warning-on-addresses-dialog v-model="showWarningOnAddressesDialog" />
55

6-
<component :is="layout">
6+
<v-main>
77
<router-view />
8-
</component>
8+
</v-main>
99
</v-app>
1010
</template>
1111

12-
<script lang="ts">
13-
import { defineComponent } from 'vue'
12+
<script setup lang="ts">
13+
import { computed, onBeforeUnmount, onMounted, getCurrentInstance, ref } from 'vue'
1414
import dayjs from 'dayjs'
1515
import WarningOnAddressesDialog from '@/components/WarningOnAddressesDialog.vue'
1616
import UploadAttachmentExitPrompt from '@/components/UploadAttachmentExitPrompt.vue'
1717
import Notifications from '@/lib/notifications'
1818
import { ThemeName } from './plugins/vuetify'
19+
import { useStore } from 'vuex'
20+
import { useI18n } from 'vue-i18n'
1921
20-
export default defineComponent({
21-
components: {
22-
WarningOnAddressesDialog,
23-
UploadAttachmentExitPrompt
24-
},
25-
data: () => ({
26-
showWarningOnAddressesDialog: false,
27-
notifications: null as Notifications | null
28-
}),
29-
computed: {
30-
layout() {
31-
return this.$route.meta.layout || 'default'
32-
},
33-
isLogged() {
34-
return this.$store.getters.isLogged
35-
},
36-
isLoginViaPassword() {
37-
return this.$store.getters['options/isLoginViaPassword']
38-
},
39-
themeName() {
40-
return this.$store.state.options.darkTheme ? ThemeName.Dark : ThemeName.Light
41-
}
42-
},
43-
created() {
44-
this.setLocale()
45-
},
46-
mounted() {
47-
this.notifications = new Notifications(this)
48-
this.notifications.start()
49-
},
50-
beforeUnmount() {
51-
this.notifications?.stop()
52-
this.$store.dispatch('stopInterval')
53-
},
54-
methods: {
55-
setLocale() {
56-
// Set language from `localStorage`.
57-
//
58-
// This is required only when initializing the application.
59-
// Subsequent mutations of `language.currentLocale`
60-
// will be synchronized with `i18n.locale`.
61-
const localeFromStorage = this.$store.state.language.currentLocale
62-
this.$i18n.locale = localeFromStorage
63-
dayjs.locale(localeFromStorage)
22+
const store = useStore()
23+
const isSnackbarShowing = computed(() => store.state.snackbar.show)
24+
25+
const showWarningOnAddressesDialog = ref(false)
26+
27+
const notifications = ref<Notifications | null>(null)
28+
29+
const themeName = computed(() => {
30+
return store.state.options.darkTheme ? ThemeName.Dark : ThemeName.Light
31+
})
32+
33+
const { locale } = useI18n()
34+
35+
onMounted(() => {
36+
const instance = getCurrentInstance()
37+
38+
if (instance) {
39+
const notifications = new Notifications(instance.proxy)
40+
notifications.start()
41+
}
42+
})
43+
44+
onBeforeUnmount(() => {
45+
notifications.value?.stop()
46+
store.dispatch('stopInterval')
47+
})
48+
49+
const onKeydownHandler = (e: KeyboardEvent) => {
50+
if (e.key === 'Escape') {
51+
if (isSnackbarShowing.value) {
52+
e.stopPropagation()
53+
store.commit('snackbar/changeState', false)
6454
}
6555
}
56+
}
57+
58+
const setLocale = () => {
59+
// Set language from `localStorage`.
60+
//
61+
// This is required only when initializing the application.
62+
// Subsequent mutations of `language.currentLocale`
63+
// will be synchronized with `i18n.locale`.
64+
const localeFromStorage = store.state.language.currentLocale
65+
locale.value = localeFromStorage
66+
dayjs.locale(localeFromStorage)
67+
}
68+
69+
onMounted(() => {
70+
window.addEventListener('keydown', onKeydownHandler, true)
6671
})
72+
73+
onBeforeUnmount(() => {
74+
window.removeEventListener('keydown', onKeydownHandler, true)
75+
})
76+
77+
setLocale()
6778
</script>
6879

6980
<style lang="scss" scoped>

src/assets/styles/components/_chat.scss

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
@use 'sass:map';
22
@use '../settings/_colors.scss';
33
@use 'vuetify/settings';
4+
@use '@/assets/styles/themes/adamant/_mixins.scss';
5+
46

57
$chat-sender: (
68
'font-size': 15px,
@@ -23,10 +25,10 @@ $scroll-bar-width: 4px;
2325
height: 100%; // <-- Fallback for Chrome < v108
2426
// Chrome supports `dvh, lvh...` units starting with v108.
2527
// See https://caniuse.com/viewport-unit-variants
26-
height: 100dvh;
28+
height: calc(100dvh - var(--v-layout-bottom));
2729
position: relative;
2830

29-
@media #{map.get(settings.$display-breakpoints, 'sm-and-down')} {
31+
@media (max-width: 799px) {
3032
position: fixed;
3133
left: 0;
3234
right: 0;
@@ -42,7 +44,6 @@ $scroll-bar-width: 4px;
4244
&__body {
4345
position: relative;
4446
flex: 1;
45-
background-color: inherit;
4647
color: inherit;
4748
}
4849

@@ -390,6 +391,10 @@ $scroll-bar-width: 4px;
390391
color: map.get(settings.$shades, 'black');
391392
}
392393

394+
&__body {
395+
@include mixins.linear-gradient-light-gray();
396+
}
397+
393398
&__message {
394399
background-color: #fff;
395400
box-shadow:
@@ -499,6 +504,10 @@ $scroll-bar-width: 4px;
499504
color: map.get(settings.$shades, 'white');
500505
}
501506

507+
&__body {
508+
@include mixins.linear-gradient-dark-soft();
509+
}
510+
502511
&__message {
503512
background-color: map.get(colors.$adm-colors, 'black');
504513
box-shadow:

src/assets/styles/generic/_layout.scss

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,3 @@
22
html {
33
overflow-y: auto;
44
}
5-
6-
.container--with-app-toolbar {
7-
padding-top: var(--toolbar-height);
8-
}

src/assets/styles/themes/adamant/_mixins.scss

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,11 @@
7272
@mixin linear-gradient-dark {
7373
background: repeating-linear-gradient(140deg, #191919, #191919 0.7px, #212121 0, #212121 5px);
7474
}
75+
76+
@mixin linear-gradient-dark-soft {
77+
background: repeating-linear-gradient(140deg, #252525, #252525 0.7px, #303030 0, #303030 5px);
78+
}
79+
80+
@mixin linear-gradient-light-gray {
81+
background: repeating-linear-gradient(140deg, #e6e6e6, #e6e6e6 0.7px, #f2f2f2 0, #f2f2f2 5px);
82+
}

src/components/AChat/AChatForm.vue

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
<template #prepend-inner>
2828
<chat-emojis
2929
@keydown.capture.esc="closeElement"
30-
:open="emojiPickerOpen"
30+
:open="isEmojiPickerOpen"
3131
@onChange="onToggleEmojiPicker"
3232
@get-emoji-picture="emojiPicture"
3333
></chat-emojis>
@@ -43,10 +43,11 @@
4343
</template>
4444

4545
<script>
46-
import { nextTick } from 'vue'
46+
import { computed, nextTick } from 'vue'
4747
import ChatEmojis from '@/components/Chat/ChatEmojis.vue'
4848
import { isMobile } from '@/lib/display-mobile'
4949
import { mdiSend } from '@mdi/js'
50+
import { useChatStateStore } from '@/stores/chat-state'
5051
5152
export default {
5253
components: { ChatEmojis },
@@ -85,14 +86,22 @@ export default {
8586
},
8687
emits: ['message', 'esc', 'error'],
8788
setup() {
89+
const chatStateStore = useChatStateStore()
90+
91+
const { setEmojiPickerOpen } = chatStateStore
92+
93+
const isEmojiPickerOpen = computed({
94+
get: () => chatStateStore.isEmojiPickerOpen,
95+
set: setEmojiPickerOpen
96+
})
8897
8998
return {
99+
isEmojiPickerOpen,
90100
mdiSend
91101
}
92102
},
93103
data: () => ({
94104
message: '',
95-
emojiPickerOpen: false,
96105
botCommandIndex: null,
97106
botCommandSelectionMode: false,
98107
isInputFocused: false
@@ -176,10 +185,10 @@ export default {
176185
}
177186
},
178187
openElement() {
179-
this.emojiPickerOpen = true
188+
this.isEmojiPickerOpen = true
180189
},
181190
closeElement() {
182-
this.emojiPickerOpen = false
191+
this.isEmojiPickerOpen = false
183192
setTimeout(() => this.focus(), 0)
184193
},
185194
onInput: function () {
@@ -221,7 +230,7 @@ export default {
221230
},
222231
223232
onToggleEmojiPicker(state) {
224-
this.emojiPickerOpen = state
233+
this.isEmojiPickerOpen = state
225234
226235
this.focus()
227236
},

src/components/AChat/AChatReactions/AChatReactions.vue

Lines changed: 40 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,20 @@
1515
:partner-id="partnerId"
1616
>
1717
<template #avatar v-if="reaction.senderId === partnerId">
18-
<ChatAvatar :user-id="partnerId" :size="16" />
18+
<chat-avatar :user-id="partnerId" :size="16" />
1919
</template>
2020
</a-chat-reaction>
2121
</div>
2222
</template>
2323

24-
<script lang="ts">
24+
<script setup lang="ts">
2525
import { usePartnerId } from '@/components/AChat/hooks/usePartnerId'
26-
import ChatAvatar from '@/components/Chat/ChatAvatar.vue'
2726
import { isEmptyReaction, NormalizedChatMessageTransaction } from '@/lib/chat/helpers'
28-
import { computed, defineComponent, PropType, watch } from 'vue'
27+
import { computed, PropType, watch } from 'vue'
2928
import { useStore } from 'vuex'
3029
import { vibrate } from '@/lib/vibrate'
3130
import AChatReaction from './AChatReaction.vue'
31+
import ChatAvatar from '@/components/Chat/ChatAvatar.vue'
3232
3333
const className = 'a-chat-reactions'
3434
const classes = {
@@ -37,57 +37,44 @@ const classes = {
3737
reaction: `${className}__reaction`
3838
}
3939
40-
export default defineComponent({
41-
components: {
42-
ChatAvatar,
43-
AChatReaction
44-
},
45-
props: {
46-
transaction: {
47-
type: Object as PropType<NormalizedChatMessageTransaction>,
48-
required: true
49-
}
50-
},
51-
setup(props) {
52-
const store = useStore()
53-
const partnerId = usePartnerId(props.transaction)
54-
55-
const myReaction = computed(() =>
56-
store.getters['chat/lastReaction'](props.transaction.id, partnerId.value, store.state.address)
57-
)
58-
const partnerReaction = computed(() =>
59-
store.getters['chat/lastReaction'](props.transaction.id, partnerId.value, partnerId.value)
60-
)
61-
62-
const displayMyReaction = computed(() => myReaction.value && !isEmptyReaction(myReaction.value))
63-
const displayPartnerReaction = computed(
64-
() => partnerReaction.value && !isEmptyReaction(partnerReaction.value)
65-
)
66-
67-
const reactions = computed(() => {
68-
const list = []
69-
70-
if (displayMyReaction.value) list.push(myReaction.value)
71-
if (displayPartnerReaction.value) list.push(partnerReaction.value)
72-
73-
return list.sort((left, right) => left.timestamp - right.timestamp)
74-
})
75-
76-
watch(
77-
() => store.getters['chat/numOfNewMessages'](partnerId.value),
78-
(numOfNewMessages) => {
79-
if (numOfNewMessages > 0) vibrate.veryShort()
80-
},
81-
{ immediate: true }
82-
)
83-
84-
return {
85-
classes,
86-
partnerId,
87-
reactions
88-
}
40+
const props = defineProps({
41+
transaction: {
42+
type: Object as PropType<NormalizedChatMessageTransaction>,
43+
required: true
8944
}
9045
})
46+
47+
const store = useStore()
48+
const partnerId = usePartnerId(props.transaction)
49+
50+
const myReaction = computed(() =>
51+
store.getters['chat/lastReaction'](props.transaction.id, partnerId.value, store.state.address)
52+
)
53+
const partnerReaction = computed(() =>
54+
store.getters['chat/lastReaction'](props.transaction.id, partnerId.value, partnerId.value)
55+
)
56+
57+
const displayMyReaction = computed(() => myReaction.value && !isEmptyReaction(myReaction.value))
58+
const displayPartnerReaction = computed(
59+
() => partnerReaction.value && !isEmptyReaction(partnerReaction.value)
60+
)
61+
62+
const reactions = computed(() => {
63+
const list = []
64+
65+
if (displayMyReaction.value) list.push(myReaction.value)
66+
if (displayPartnerReaction.value) list.push(partnerReaction.value)
67+
68+
return list.sort((left, right) => left.timestamp - right.timestamp)
69+
})
70+
71+
watch(
72+
() => store.getters['chat/numOfNewMessages'](partnerId.value),
73+
(numOfNewMessages) => {
74+
if (numOfNewMessages > 0) vibrate.veryShort()
75+
},
76+
{ immediate: true }
77+
)
9178
</script>
9279

9380
<style lang="scss">

0 commit comments

Comments
 (0)