Skip to content

Commit

Permalink
Web Vitals | Optimize Performance
Browse files Browse the repository at this point in the history
  • Loading branch information
kvestus committed Mar 10, 2024
1 parent 502f00d commit d28207a
Show file tree
Hide file tree
Showing 34 changed files with 103 additions and 88 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:

jobs:
build:
timeout-minutes: 3
timeout-minutes: 5
runs-on: ubuntu-latest
container:
image: cypress/browsers:node18.12.0-chrome106-ff106
Expand Down
7 changes: 2 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,7 @@ Check out the live demo of the app at https://languages-learner.web.app/

## Preview (Mobile)

[//]: # (![preview-dictionary-page-mobile](public/preview-dictionary-page-mobile.png))

[//]: # (![preview-profile-page-mobile](public/preview-profile-page-mobile.png))
<p align="center">
<img src="public/preview-dictionary-page-mobile.png" alt="preview-dictionary-page-mobile" width="200" style="margin-right: 20px;">
<img src="public/preview-profile-page-mobile.png" alt="preview-profile-page-mobile" width="200">
<img src="src/assets/images/preview-dictionary-page-mobile.webp" alt="preview-dictionary-page-mobile" width="200" style="margin-right: 20px;">
<img src="src/assets/images/preview-profile-page-mobile.webp" alt="preview-profile-page-mobile" width="200">
</p>
31 changes: 22 additions & 9 deletions cypress/e2e/auth.cy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// <reference types="cypress" />

import { elSelector, withLang } from '@@/cypress/utils'
import { withLang } from '@@/cypress/utils'
import { EDataTest, EDataTestClass } from '@/enums/EDataTest'

describe('user sign-in, sign-up and logout', () => {
Expand All @@ -20,9 +20,12 @@ describe('user sign-in, sign-up and logout', () => {
cy
.el(EDataTest.landing_sign_in_button).click()
// To make close button unfocused
.elByClass(EDataTestClass.app_card_content).should('be.visible').click()
.elByClass(EDataTestClass.app_dialog).should('be.visible').click()
.get('html').toMatchSnapshot('Sign in modal')
.get(`${elSelector(EDataTest.authentication_modal)} .n-base-close`).click()
.elByClass(EDataTestClass.app_dialog).within(() => {
cy.elByClass(EDataTestClass.app_close_button).click()
})


.authWithoutSession()
.elByClass(EDataTestClass.app_notifications).should('be.visible').and('contain', 'successful_authorization')
Expand Down Expand Up @@ -53,7 +56,9 @@ describe('user sign-in, sign-up and logout', () => {
.toMatchSnapshotForEl(EDataTestClass.app_notifications, 'Sign in error notification')
cy
.el(EDataTest.authentication_modal_error).should('be.visible').contains(errorMessageInvalidEmail)
.get(`${elSelector(EDataTest.authentication_modal)} .n-base-close`).click()
.elByClass(EDataTestClass.app_dialog).within(() => {
cy.elByClass(EDataTestClass.app_close_button).click()
})

cy.authWithoutSession({
username: '',
Expand All @@ -68,7 +73,9 @@ describe('user sign-in, sign-up and logout', () => {
.and('contain', errorMessageInvalidEmail)
cy
.el(EDataTest.authentication_modal_error).should('be.visible').contains(errorMessageInvalidEmail)
.get(`${elSelector(EDataTest.authentication_modal)} .n-base-close`).click()
.elByClass(EDataTestClass.app_dialog).within(() => {
cy.elByClass(EDataTestClass.app_close_button).click()
})

cy.authWithoutSession({
username: '[email protected]',
Expand All @@ -83,7 +90,9 @@ describe('user sign-in, sign-up and logout', () => {
.and('contain', errorMessageInvalidCredential)
cy
.el(EDataTest.authentication_modal_error).should('be.visible').contains(errorMessageInvalidCredential)
.get(`${elSelector(EDataTest.authentication_modal)} .n-base-close`).click()
.elByClass(EDataTestClass.app_dialog).within(() => {
cy.elByClass(EDataTestClass.app_close_button).click()
})

cy.authWithoutSession({
username: '[email protected]',
Expand All @@ -98,16 +107,20 @@ describe('user sign-in, sign-up and logout', () => {
.and('contain', errorMessageMissingPassword)
cy
.el(EDataTest.authentication_modal_error).should('be.visible').contains(errorMessageMissingPassword)
.get(`${elSelector(EDataTest.authentication_modal)} .n-base-close`).click()
.elByClass(EDataTestClass.app_dialog).within(() => {
cy.elByClass(EDataTestClass.app_close_button).click()
})
})

it('should display sign-up error for existing use', () => {
cy
.el(EDataTest.landing_sign_up_button).click()
// To make close button unfocused
.elByClass(EDataTestClass.app_card_content).should('be.visible').click()
.elByClass(EDataTestClass.app_dialog).should('be.visible').click()
.get('html').toMatchSnapshot('Sign up modal')
.get(`${elSelector(EDataTest.authentication_modal)} .n-base-close`).click()
.elByClass(EDataTestClass.app_dialog).within(() => {
cy.elByClass(EDataTestClass.app_close_button).click()
})

.authWithoutSession({
...(Cypress.env('testUser') ?? {}),
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="192x192" href="/android-chrome-192x192.png" />
<link rel="icon" type="image/png" sizes="512x512" href="/android-chrome-512x512.png" />
<link rel="preconnect" href="https://firestore.googleapis.com" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1" />
<title>Languages Learner</title>
</head>
Expand Down
7 changes: 3 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
"jsdom": "^24.0.0",
"lint-staged": "^15.2.1",
"lodash.debounce": "^4.0.8",
"naive-ui": "^2.34.3",
"naive-ui": "^2.38.1",
"postcss": "^8.4.5",
"postcss-html": "^1.3.0",
"sass": "^1.45.0",
Expand Down
Binary file removed public/preview-dictionary-page-mobile.png
Binary file not shown.
Binary file removed public/preview-profile-page-mobile.png
Binary file not shown.
4 changes: 3 additions & 1 deletion src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ initializeOnAuthStateChangedHook(router)
<n-notification-provider
placement="bottom-left"
container-class="app-notifications">
<router-view></router-view>
<n-modal-provider>
<router-view></router-view>
</n-modal-provider>
</n-notification-provider>
</n-dialog-provider>
</template>
Binary file added src/assets/images/main-page-background.webp
Binary file not shown.
Binary file not shown.
Binary file not shown.
File renamed without changes
1 change: 1 addition & 0 deletions src/enums/EDataTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export enum EDataTestClass {
app_checkbox_checked = 'n-checkbox--checked',
app_checkbox_disabled = 'n-checkbox--disabled',
app_card_content = 'n-card__content',
app_close_button = 'n-base-close',

word_status = 'data-test__word_status',

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<script setup lang="ts">
import { useElementSize } from '@vueuse/core'
import LandingAbout from '@/modules/landing/components/LandingAbout/LandingAbout.vue'
import PreviewProfilePageMobile from '@/assets/images/preview-profile-page-mobile.webp'
import PreviewDictionaryPageMobile from '@/assets/images/preview-dictionary-page-mobile.webp'
const { isTablet, isMobile } = useAppBreakpoints()
Expand Down Expand Up @@ -61,12 +63,12 @@ const carouselStyle = computed(() => unref(isTablet) || unref(isMobile) ?
centered-slides>
<n-carousel-item>
<img
src="/preview-dictionary-page-mobile.png"
:src="PreviewDictionaryPageMobile"
alt="preview-dictionary-page-mobile"/>
</n-carousel-item>
<n-carousel-item>
<img
src="/preview-profile-page-mobile.png"
:src="PreviewProfilePageMobile"
alt="preview-profile-page-mobile"/>
</n-carousel-item>
</n-carousel>
Expand Down
20 changes: 10 additions & 10 deletions src/modules/landing/components/LandingHeader/LandingHeader.vue
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
<script setup lang="ts">
import { useModal } from 'naive-ui'
import { EAuthenticateModalType } from '@/modules/landing/modules/authenticate/components/AuthenticateModal/EAuthenticateModalType'
import InterfaceLanguageSelector from '@/components/InterfaceLanguageSelector/InterfaceLanguageSelector.vue'
import AuthenticateModal from '@/modules/landing/modules/authenticate/components/AuthenticateModal/AuthenticateModal.vue'
const modal = useModal()
const { isLoggedIn } = storeToRefs(useUserStore())
const authenticateModalType = ref<EAuthenticateModalType>(EAuthenticateModalType.SIGNIN)
const isNeededToShowAuthenticateModal = ref(false)
const showAuthenticateModal = (type: EAuthenticateModalType) => {
authenticateModalType.value = type
isNeededToShowAuthenticateModal.value = true
const showAuthenticateModal = async (type: EAuthenticateModalType) => {
const { useAuthenticateModal } = await import('@/modules/landing/modules/authenticate/components/AuthenticateModal/useAuthenticateModal')
const { openAuthenticateModal } = useAuthenticateModal(modal)
openAuthenticateModal(type)
}
onBeforeUnmount(() => {
modal.destroyAll()
})
</script>

<template>
Expand Down Expand Up @@ -43,9 +46,6 @@ const showAuthenticateModal = (type: EAuthenticateModalType) => {
</router-link>
</n-button>
</n-space>
<AuthenticateModal
v-model:show="isNeededToShowAuthenticateModal"
:type="authenticateModalType"/>
</div>
</template>

Expand Down
2 changes: 1 addition & 1 deletion src/modules/landing/layouts/landing-layout.scss
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.landing-layout {
width: 100svw;
height: 100dvh;
background-image: url(/main-page-background.jpg);
background-image: url(/src/assets/images/main-page-background.webp);
background-repeat: no-repeat;
background-position: center center;
background-size: cover;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,14 @@ import { EAuthenticateModalType } from '@/modules/landing/modules/authenticate/c
const props = defineProps<{
type: EAuthenticateModalType
show: boolean
}>()
const { isMobile } = useAppBreakpoints()
const authenticateType = ref(props.type)
watch(() => props.type, () => authenticateType.value = props.type)
const modalStyle = computed(() => unref(isMobile)
? {
width: '100vw',
height: '100dvh',
}
: {
width: '600px',
})
const emit = defineEmits<{
(e: 'update:show', value: boolean): void
}>()
const updateShow = (value: boolean) => emit('update:show', value)
</script>

<template>
<n-modal
:show="props.show"
:on-update:show="updateShow"
transform-origin="center"
preset="card"
size="huge"
:title="$t('authentication')"
:style="modalStyle"
:data-test="EDataTest.authentication_modal"
>
<div :data-test="EDataTest.authentication_modal">
<n-tabs
v-model:value="authenticateType"
:default-value="EAuthenticateModalType.SIGNIN"
Expand All @@ -58,5 +31,5 @@ const updateShow = (value: boolean) => emit('update:show', value)
<AuthenticateModalForm :type="EAuthenticateModalType.SIGNUP"/>
</n-tab-pane>
</n-tabs>
</n-modal>
</div>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { defineAsyncComponent } from 'vue'
import { type ModalReactive } from 'naive-ui'
import { type ModalApiInjection } from 'naive-ui/lib/modal/src/ModalProvider'
import { type EAuthenticateModalType } from '@/modules/landing/modules/authenticate/components/AuthenticateModal/EAuthenticateModalType'
const AuthenticateModal = defineAsyncComponent(() => import('@/modules/landing/modules/authenticate/components/AuthenticateModal/AuthenticateModal.vue'))

export const useAuthenticateModal = (modal: ModalApiInjection) => {
const { t } = useI18n()
const modalInst = ref<ModalReactive>()

const openAuthenticateModal = (type: EAuthenticateModalType) => {
modalInst.value = modal.create({
title: t('authentication'),
content: () => h(AuthenticateModal, { type }),
preset: 'dialog',
transformOrigin: 'center',
showIcon: false,
})
}

return {
openAuthenticateModal,
}
}
2 changes: 1 addition & 1 deletion src/modules/workspace/layouts/WorkspaceLayout.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
import WorkspaceHeader from '@/modules/workspace/components/WorkspaceHeader/WorkspaceHeader.vue'
import WorkspaceBottomMenu from '@/modules/workspace/components/WorkspaceBottomMenu/WorkspaceBottomMenu.vue'
const WorkspaceBottomMenu = defineAsyncComponent(() => import('@/modules/workspace/components/WorkspaceBottomMenu/WorkspaceBottomMenu.vue'))
const { isMobile } = useAppBreakpoints()
</script>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { type ComputedRef } from 'vue'
import OfficeNavigation from '@/modules/workspace/modules/office/components/OfficeNavigation/OfficeNavigation.vue'
const OfficeNavigation = defineAsyncComponent(() => import('@/modules/workspace/modules/office/components/OfficeNavigation/OfficeNavigation.vue'))
const { t } = useI18n()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
&__user {
height: 400px;
background-color: #fff;
background-image: url(/profile-background.svg);
background-image: url(/src/assets/images/profile-background.svg);
background-repeat: no-repeat;
background-position-y: 102%;
background-size: 100%;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import InterfaceLanguageSelector from '@/components/InterfaceLanguageSelector/InterfaceLanguageSelector.vue'
const InterfaceLanguageSelector = defineAsyncComponent(() => import('@/components/InterfaceLanguageSelector/InterfaceLanguageSelector.vue'))
const { isUserDataLoaded, customData } = storeToRefs(useUserStore())
const { updateNativeLanguage } = useUserStore()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts" setup>
import WordsContainerHeaderStatuses from '@/modules/workspace/modules/words/components/WordsContainer/components/WordsContainerHeader/WordsContainerHeaderStatuses/WordsContainerHeaderStatuses.vue'
import WordsContainerAddWordButton from '@/modules/workspace/modules/words/components/WordsContainer/components/WordsContainerAddWordButton/WordsContainerAddWordButton.vue'
const WordsContainerAddWordButton = defineAsyncComponent(() => import('@/modules/workspace/modules/words/components/WordsContainer/components/WordsContainerAddWordButton/WordsContainerAddWordButton.vue'))
import { EWordsContainerState, useWordsContainerStore } from '@/modules/workspace/modules/words/components/WordsContainer/useWordsContainerStore'
const { isMobile } = useAppBreakpoints()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
import type { WordsFilters } from '@/modules/workspace/modules/words/types/WordsFilters'
import WordStatusIcon from '@/modules/workspace/modules/words/components/WordStatus/WordStatusIcon.vue'
const WordStatusIcon = defineAsyncComponent(() => import('@/modules/workspace/modules/words/components/WordStatus/WordStatusIcon.vue'))
import { useWordStatuses } from '@/modules/workspace/modules/words/composables/useWordStatuses'
const { isMobile } = useAppBreakpoints()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
import VirtualList from 'vue3-virtual-scroll-list'
import WordsContainerWordsListItem from '@/modules/workspace/modules/words/components/WordsContainer/components/WordsContainerWordsList/components/WordsContainerWordsListItem/WordsContainerWordsListItem.vue'
import WordsContainerWordsListAddBlock from '@/modules/workspace/modules/words/components/WordsContainer/components/WordsContainerWordsList/components/WordsContainerWordsListAddBlock/WordsContainerWordsListAddBlock.vue'
import WordsContainerAddWordButton from '@/modules/workspace/modules/words/components/WordsContainer/components/WordsContainerAddWordButton/WordsContainerAddWordButton.vue'
const WordsContainerWordsListItem = defineAsyncComponent(() => import('@/modules/workspace/modules/words/components/WordsContainer/components/WordsContainerWordsList/components/WordsContainerWordsListItem/WordsContainerWordsListItem.vue'))
const WordsContainerWordsListAddBlock = defineAsyncComponent(() => import('@/modules/workspace/modules/words/components/WordsContainer/components/WordsContainerWordsList/components/WordsContainerWordsListAddBlock/WordsContainerWordsListAddBlock.vue'))
const WordsContainerAddWordButton = defineAsyncComponent(() => import('@/modules/workspace/modules/words/components/WordsContainer/components/WordsContainerAddWordButton/WordsContainerAddWordButton.vue'))
import { EWordsContainerState, useWordsContainerStore } from '@/modules/workspace/modules/words/components/WordsContainer/useWordsContainerStore'
import { type WordsContainerWordsListItemProps } from '@/modules/workspace/modules/words/components/WordsContainer/components/WordsContainerWordsList/components/WordsContainerWordsListItem/types'
Expand Down Expand Up @@ -68,20 +68,22 @@ const isWordsNeeded = computed(() => isWordsContainerState([
<WordsContainerWordsListAddBlock
v-if="isAddBlockNeeded"
class="words-container-words-list__item" />
<virtual-list
v-if="isWordsNeeded"
@deleteWord="deleteWord"
@updateWordStatus="updateWordStatus"
@updateWordTranslations="updateWordTranslations"
@toggleWordSelection="toggleWordSelection"
data-key="word"
:data-sources="items"
:data-component="WordsContainerWordsListItem"
:data-test="EDataTest.words_list"
:estimate-size="77"
item-class="words-container-words-list__item"
:page-mode="true"
/>
<Suspense>
<virtual-list
v-if="isWordsNeeded"
@deleteWord="deleteWord"
@updateWordStatus="updateWordStatus"
@updateWordTranslations="updateWordTranslations"
@toggleWordSelection="toggleWordSelection"
data-key="word"
:data-sources="items"
:data-component="WordsContainerWordsListItem"
:data-test="EDataTest.words_list"
:estimate-size="77"
item-class="words-container-words-list__item"
:page-mode="true"
/>
</Suspense>
<template v-if="isOtherWordsNeeded">
<div class="words-container-words-list__other-words">
<n-text
Expand Down
Loading

0 comments on commit d28207a

Please sign in to comment.