feat: convert navigation to script setup and ts

This commit is contained in:
Dominik Pschenitschni 2022-02-13 18:11:26 +01:00 committed by kolaente
parent b5f867cc66
commit 658ca4c955
No known key found for this signature in database
GPG key ID: F40E70337AB24C9B
8 changed files with 149 additions and 123 deletions

View file

@ -23,6 +23,7 @@
"@sentry/tracing": "6.19.3", "@sentry/tracing": "6.19.3",
"@sentry/vue": "6.19.3", "@sentry/vue": "6.19.3",
"@types/is-touch-device": "1.0.0", "@types/is-touch-device": "1.0.0",
"@types/sortablejs": "^1.10.7",
"@vue/compat": "3.2.31", "@vue/compat": "3.2.31",
"@vueuse/core": "8.2.3", "@vueuse/core": "8.2.3",
"@vueuse/router": "8.2.3", "@vueuse/router": "8.2.3",

View file

@ -156,95 +156,90 @@
</aside> </aside>
</template> </template>
<script lang="ts"> <script setup lang="ts">
import {defineComponent} from 'vue' import {ref, computed, onMounted, onBeforeMount} from 'vue'
import {useStore} from 'vuex'
import {mapState} from 'vuex'
import draggable from 'vuedraggable' import draggable from 'vuedraggable'
import { SortableEvent } from 'sortablejs'
import ListSettingsDropdown from '@/components/list/list-settings-dropdown.vue' import ListSettingsDropdown from '@/components/list/list-settings-dropdown.vue'
import NamespaceSettingsDropdown from '@/components/namespace/namespace-settings-dropdown.vue' import NamespaceSettingsDropdown from '@/components/namespace/namespace-settings-dropdown.vue'
import PoweredByLink from '@/components/home/PoweredByLink.vue' import PoweredByLink from '@/components/home/PoweredByLink.vue'
import Logo from '@/components/home/Logo.vue' import Logo from '@/components/home/Logo.vue'
import {CURRENT_LIST, MENU_ACTIVE, LOADING, LOADING_MODULE} from '@/store/mutation-types' import {MENU_ACTIVE} from '@/store/mutation-types'
import {calculateItemPosition} from '@/helpers/calculateItemPosition' import {calculateItemPosition} from '@/helpers/calculateItemPosition'
import {getNamespaceTitle} from '@/helpers/getNamespaceTitle'
import { useEventListener } from '@vueuse/core'
import NamespaceModel from '@/models/namespace'
import ListModel from '@/models/list'
const drag = ref(false)
export default defineComponent({ const dragOptions = {
name: 'navigation',
components: {
ListSettingsDropdown,
NamespaceSettingsDropdown,
draggable,
Logo,
PoweredByLink,
},
data() {
return {
listsVisible: {},
drag: false,
dragOptions: {
animation: 100, animation: 100,
ghostClass: 'ghost', ghostClass: 'ghost',
}, }
listUpdating: {},
} const store = useStore()
}, const currentList = computed(() => store.state.currentList)
computed: { const menuActive = computed(() => store.state.menuActive)
...mapState({ const loading = computed(() => store.state.loading && store.state.loadingModule === 'namespaces')
namespaces: state => state.namespaces.namespaces.filter(n => !n.isArchived),
currentList: CURRENT_LIST,
background: 'background', const namespaces = computed(() => {
menuActive: MENU_ACTIVE, return (store.state.namespaces.namespaces as NamespaceModel[]).filter(n => !n.isArchived)
loading: state => state[LOADING] && state[LOADING_MODULE] === 'namespaces', })
}), const activeLists = computed(() => {
activeLists() { return namespaces.value.map(({lists}) => {
return this.namespaces.map(({lists}) => lists?.filter(item => typeof item !== 'undefined' && !item.isArchived)) return lists?.filter(item => {
}, return typeof item !== 'undefined' && !item.isArchived
namespaceTitles() {
return this.namespaces.map((namespace) => this.getNamespaceTitle(namespace))
},
namespaceListsCount() {
return this.namespaces.map((_, index) => this.activeLists[index]?.length ?? 0)
},
},
beforeCreate() {
// FIXME: async action in beforeCreate, might be unfinished when component mounts
this.$store.dispatch('namespaces/loadNamespaces')
.then(namespaces => {
namespaces.forEach(n => {
if (typeof this.listsVisible[n.id] === 'undefined') {
this.listsVisible[n.id] = true
}
}) })
}) })
}, })
created() {
window.addEventListener('resize', this.resize) const namespaceTitles = computed(() => {
}, return namespaces.value.map((namespace) => getNamespaceTitle(namespace))
mounted() { })
this.resize()
}, const namespaceListsCount = computed(() => {
methods: { return namespaces.value.map((_, index) => activeLists.value[index]?.length ?? 0)
toggleFavoriteList(list) { })
useEventListener('resize', resize)
onMounted(() => resize())
function toggleFavoriteList(list: ListModel) {
// The favorites pseudo list is always favorite // The favorites pseudo list is always favorite
// Archived lists cannot be marked favorite // Archived lists cannot be marked favorite
if (list.id === -1 || list.isArchived) { if (list.id === -1 || list.isArchived) {
return return
} }
this.$store.dispatch('lists/toggleListFavorite', list) store.dispatch('lists/toggleListFavorite', list)
}, }
resize() {
function resize() {
// Hide the menu by default on mobile // Hide the menu by default on mobile
this.$store.commit(MENU_ACTIVE, window.innerWidth >= 770) store.commit(MENU_ACTIVE, window.innerWidth >= 770)
}, }
toggleLists(namespaceId) {
this.listsVisible[namespaceId] = !this.listsVisible[namespaceId] function toggleLists(namespaceId: number) {
}, listsVisible.value[namespaceId] = !listsVisible.value[namespaceId]
updateActiveLists(namespace, activeLists) { }
const listsVisible = ref<{ [id: NamespaceModel['id']]: boolean}>({})
// FIXME: async action will be unfinished when component mounts
onBeforeMount(async () => {
const namespaces = await store.dispatch('namespaces/loadNamespaces') as NamespaceModel[]
namespaces.forEach(n => {
if (typeof listsVisible.value[n.id] === 'undefined') {
listsVisible.value[n.id] = true
}
})
})
function updateActiveLists(namespace: NamespaceModel, activeLists: ListModel[]) {
// This is a bit hacky: since we do have to filter out the archived items from the list // This is a bit hacky: since we do have to filter out the archived items from the list
// for vue draggable updating it is not as simple as replacing it. // for vue draggable updating it is not as simple as replacing it.
// To work around this, we merge the active lists with the archived ones. Doing so breaks the order // To work around this, we merge the active lists with the archived ones. Doing so breaks the order
@ -255,39 +250,41 @@ export default defineComponent({
...namespace.lists.filter(l => l.isArchived), ...namespace.lists.filter(l => l.isArchived),
] ]
const newNamespace = { store.commit('namespaces/setNamespaceById', {
...namespace, ...namespace,
lists, lists,
} })
}
this.$store.commit('namespaces/setNamespaceById', newNamespace) const listUpdating = ref<{ [id: NamespaceModel['id']]: boolean}>({})
}, async function saveListPosition(e: SortableEvent) {
if (!e.newIndex) return
async saveListPosition(e) { const namespaceId = parseInt(e.to.dataset.namespaceId as string)
const namespaceId = parseInt(e.to.dataset.namespaceId) const newNamespaceIndex = parseInt(e.to.dataset.namespaceIndex as string)
const newNamespaceIndex = parseInt(e.to.dataset.namespaceIndex)
const listsActive = this.activeLists[newNamespaceIndex] const listsActive = activeLists.value[newNamespaceIndex]
const list = listsActive[e.newIndex] const list = listsActive[e.newIndex]
const listBefore = listsActive[e.newIndex - 1] ?? null const listBefore = listsActive[e.newIndex - 1] ?? null
const listAfter = listsActive[e.newIndex + 1] ?? null const listAfter = listsActive[e.newIndex + 1] ?? null
this.listUpdating[list.id] = true listUpdating.value[list.id] = true
const position = calculateItemPosition(listBefore !== null ? listBefore.position : null, listAfter !== null ? listAfter.position : null) const position = calculateItemPosition(
listBefore !== null ? listBefore.position : null,
listAfter !== null ? listAfter.position : null,
)
try { try {
// create a copy of the list in order to not violate vuex mutations // create a copy of the list in order to not violate vuex mutations
await this.$store.dispatch('lists/updateList', { await store.dispatch('lists/updateList', {
...list, ...list,
position, position,
namespaceId, namespaceId,
}) })
} finally { } finally {
this.listUpdating[list.id] = false listUpdating.value[list.id] = false
} }
}, }
},
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View file

@ -1,6 +1,7 @@
import {i18n} from '@/i18n' import {i18n} from '@/i18n'
import NamespaceModel from '@/models/namespace'
export const getNamespaceTitle = (n) => { export const getNamespaceTitle = (n: NamespaceModel) => {
if (n.id === -1) { if (n.id === -1) {
return i18n.global.t('namespace.pseudo.sharedLists.title') return i18n.global.t('namespace.pseudo.sharedLists.title')
} }

View file

@ -8,6 +8,9 @@ export default class AttachmentModel extends AbstractModel {
this.createdBy = new UserModel(this.createdBy) this.createdBy = new UserModel(this.createdBy)
this.file = new FileModel(this.file) this.file = new FileModel(this.file)
this.created = new Date(this.created) this.created = new Date(this.created)
/** @type {number} */
this.id
} }
defaults() { defaults() {

View file

@ -1,7 +1,7 @@
import AbstractModel from './abstractModel' import AbstractModel from './abstractModel'
import UserModel from './user' import UserModel from './user'
export default class ListModel extends AbstractModel { export default class LinkShareModel extends AbstractModel {
constructor(data) { constructor(data) {
// The constructor of AbstractModel handles all the default parsing. // The constructor of AbstractModel handles all the default parsing.

View file

@ -24,6 +24,15 @@ export default class ListModel extends AbstractModel {
this.subscription = new SubscriptionModel(this.subscription) this.subscription = new SubscriptionModel(this.subscription)
} }
/** @type {number} */
this.id
/** @type {boolean} */
this.isArchived
/** @type {number} */
this.position
this.created = new Date(this.created) this.created = new Date(this.created)
this.updated = new Date(this.updated) this.updated = new Date(this.updated)
} }

View file

@ -11,6 +11,7 @@ export default class NamespaceModel extends AbstractModel {
this.hexColor = '#' + this.hexColor this.hexColor = '#' + this.hexColor
} }
/** @type {ListModel[]} */
this.lists = this.lists.map(l => { this.lists = this.lists.map(l => {
return new ListModel(l) return new ListModel(l)
}) })
@ -21,6 +22,15 @@ export default class NamespaceModel extends AbstractModel {
this.subscription = new SubscriptionModel(this.subscription) this.subscription = new SubscriptionModel(this.subscription)
} }
/** @type {number} */
this.id
/** @type {string} */
this.title
/** @type {boolean} */
this.isArchived
this.created = new Date(this.created) this.created = new Date(this.created)
this.updated = new Date(this.updated) this.updated = new Date(this.updated)
} }

View file

@ -3024,6 +3024,11 @@
resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.3.tgz#ff5e2f1902969d305225a047c8a0fd5c915cebef" resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.3.tgz#ff5e2f1902969d305225a047c8a0fd5c915cebef"
integrity sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ== integrity sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==
"@types/sortablejs@^1.10.7":
version "1.10.7"
resolved "https://registry.yarnpkg.com/@types/sortablejs/-/sortablejs-1.10.7.tgz#ab9039c85429f0516955ec6dbc0bb20139417b15"
integrity sha512-lGCwwgpj8zW/ZmaueoPVSP7nnc9t8VqVWXS+ASX3eoUUENmiazv0rlXyTRludXzuX9ALjPsMqBu85TgJNWbTOg==
"@types/tern@*": "@types/tern@*":
version "0.23.4" version "0.23.4"
resolved "https://registry.yarnpkg.com/@types/tern/-/tern-0.23.4.tgz#03926eb13dbeaf3ae0d390caf706b2643a0127fb" resolved "https://registry.yarnpkg.com/@types/tern/-/tern-0.23.4.tgz#03926eb13dbeaf3ae0d390caf706b2643a0127fb"