split from page components
This commit is contained in:
parent
a48186fa63
commit
694ee7e89f
@ -13,9 +13,11 @@ import {
|
||||
Card,
|
||||
TextLogo,
|
||||
PlusIcon,
|
||||
HamburgerIcon,
|
||||
} from 'omorphia'
|
||||
|
||||
import { useLoading, useTheming } from '@/store/state'
|
||||
import { useInstances } from '@/store/instances'
|
||||
// import AccountsCard from './components/ui/AccountsCard.vue'
|
||||
import AccountDropdown from '@/components/ui/platform/AccountDropdown.vue'
|
||||
import InstanceCreationModal from '@/components/ui/InstanceCreationModal.vue'
|
||||
@ -38,6 +40,7 @@ import { useDisableClicks } from '@/composables/click.js'
|
||||
import { openExternal } from '@/helpers/external.js'
|
||||
import { await_sync, check_safe_loading_bars_complete } from '@/helpers/state.js'
|
||||
import { install_from_file } from '@/helpers/pack.js'
|
||||
import { iconPathAsUrl } from '@/helpers/icon'
|
||||
|
||||
import URLConfirmModal from '@/components/ui/URLConfirmModal.vue'
|
||||
import StickyTitleBar from '@/components/ui/tutorial/StickyTitleBar.vue'
|
||||
@ -50,6 +53,7 @@ import { TauriEvent } from '@tauri-apps/api/event'
|
||||
import { confirm } from '@tauri-apps/api/dialog'
|
||||
import { type } from '@tauri-apps/api/os'
|
||||
import { appWindow } from '@tauri-apps/api/window'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
const themeStore = useTheming()
|
||||
const urlModal = ref(null)
|
||||
@ -68,6 +72,9 @@ const onboardingVideo = ref()
|
||||
const failureText = ref(null)
|
||||
const os = ref('')
|
||||
|
||||
const instances = useInstances()
|
||||
const { instancesByPlayed } = storeToRefs(instances)
|
||||
|
||||
defineExpose({
|
||||
initialize: async () => {
|
||||
isLoading.value = false
|
||||
@ -275,15 +282,8 @@ const toggleSidebar = () => {
|
||||
>
|
||||
<div class="pages-list">
|
||||
<div class="square-collapsed-space">
|
||||
<Button
|
||||
v-tooltip="'Toggle sidebar'"
|
||||
transparent
|
||||
icon-only
|
||||
class="collapsed-button"
|
||||
@click="toggleSidebar"
|
||||
>
|
||||
<PlusIcon />
|
||||
<span class="collapsed-button__label">Collapse</span>
|
||||
<Button transparent icon-only class="collapsed-button" @click="toggleSidebar">
|
||||
<HamburgerIcon />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@ -316,9 +316,23 @@ const toggleSidebar = () => {
|
||||
</suspense>
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider">
|
||||
<hr />
|
||||
</div>
|
||||
<div class="instances pages-list">
|
||||
<RouterLink v-tooltip="'Meow'" to="/undefined" class="btn icon-only collapsed-button">
|
||||
Meow
|
||||
<RouterLink
|
||||
v-for="instance in instancesByPlayed"
|
||||
:key="instance.id"
|
||||
v-tooltip="instance.metadata.name"
|
||||
:to="`/instance/${encodeURIComponent(instance.path)}`"
|
||||
class="btn icon-only collapsed-button"
|
||||
>
|
||||
<img
|
||||
class="collapsed-button__icon"
|
||||
:src="iconPathAsUrl(instance.metadata?.icon)"
|
||||
:alt="instance.metadata.name"
|
||||
/>
|
||||
<span class="collapsed-button__label">{{ instance.metadata.name }}</span>
|
||||
</RouterLink>
|
||||
</div>
|
||||
<div class="settings pages-list">
|
||||
@ -358,7 +372,6 @@ const toggleSidebar = () => {
|
||||
<TextLogo class="logo" :animate="false" />
|
||||
</router-link>
|
||||
<Breadcrumbs after-logo data-tauri-drag-region />
|
||||
<!-- <pre><code>{{ JSON.stringify(breadcrumbs.path) }}</code></pre> -->
|
||||
</section>
|
||||
<section class="mod-stats">
|
||||
<Suspense>
|
||||
@ -467,6 +480,8 @@ const toggleSidebar = () => {
|
||||
.container {
|
||||
--appbar-height: 4.5rem;
|
||||
|
||||
--sidebar-gap: 0.35rem;
|
||||
|
||||
--sidebar-width: 4.5rem;
|
||||
--sidebar-open-width: 15rem;
|
||||
--sidebar-padding: 0.75rem;
|
||||
@ -602,9 +617,34 @@ const toggleSidebar = () => {
|
||||
}
|
||||
}
|
||||
|
||||
.divider {
|
||||
height: auto;
|
||||
width: 100%;
|
||||
|
||||
hr {
|
||||
background-color: var(--color-button-bg);
|
||||
border: none;
|
||||
color: var(--color-button-bg);
|
||||
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
margin-top: var(--sidebar-gap);
|
||||
// div should always have + 1 --sidebar-gap margin to the bottom to be equal
|
||||
margin-bottom: calc(var(--sidebar-gap) * 2);
|
||||
|
||||
padding-left: var(--sidebar-padding);
|
||||
padding-right: var(--sidebar-padding);
|
||||
}
|
||||
|
||||
.instances {
|
||||
height: 100%;
|
||||
flex-grow: 1;
|
||||
flex: 1;
|
||||
|
||||
flex-flow: column wrap; // This hides any elements that aren't fully visible
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.pages-list {
|
||||
@ -615,7 +655,7 @@ const toggleSidebar = () => {
|
||||
|
||||
width: 100%;
|
||||
|
||||
gap: 0.35rem;
|
||||
gap: var(--sidebar-gap);
|
||||
|
||||
.page-item,
|
||||
a {
|
||||
@ -655,6 +695,8 @@ const toggleSidebar = () => {
|
||||
height: var(--sidebar-button-size);
|
||||
width: 100%;
|
||||
|
||||
flex-shrink: 0;
|
||||
|
||||
padding: var(--sidebar-padding) !important;
|
||||
border-radius: 99999px;
|
||||
box-shadow: none;
|
||||
@ -670,6 +712,8 @@ const toggleSidebar = () => {
|
||||
height: var(--sidebar-icon-size);
|
||||
|
||||
flex-shrink: 0;
|
||||
|
||||
border-radius: var(--radius-xs);
|
||||
}
|
||||
|
||||
.collapsed-button__label {
|
||||
|
||||
12
theseus_gui/src/helpers/icon.js
Normal file
12
theseus_gui/src/helpers/icon.js
Normal file
@ -0,0 +1,12 @@
|
||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||
|
||||
export const iconPathAsUrl = (iconPath) => {
|
||||
if (!iconPath) {
|
||||
return ''
|
||||
}
|
||||
const startsWithHttp = iconPath.startsWith('http')
|
||||
if (startsWithHttp) {
|
||||
return iconPath
|
||||
}
|
||||
return convertFileSrc(iconPath)
|
||||
}
|
||||
@ -1,14 +1,13 @@
|
||||
<script setup>
|
||||
import { ref, onUnmounted, shallowRef, computed } from 'vue'
|
||||
import { ref, onUnmounted, computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import RowDisplay from '@/components/RowDisplay.vue'
|
||||
import { list } from '@/helpers/profile.js'
|
||||
import { offline_listener, profile_listener } from '@/helpers/events'
|
||||
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||
import { useFetch } from '@/helpers/fetch.js'
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
import dayjs from 'dayjs'
|
||||
import { isOffline } from '@/helpers/utils'
|
||||
import { useInstances } from '@/store/instances'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
const featuredModpacks = ref({})
|
||||
const featuredMods = ref({})
|
||||
@ -19,18 +18,17 @@ const breadcrumbs = useBreadcrumbs()
|
||||
|
||||
breadcrumbs.setRootContext({ name: 'Home', link: route.path })
|
||||
|
||||
const recentInstances = shallowRef([])
|
||||
|
||||
const offline = ref(await isOffline())
|
||||
|
||||
const getInstances = async () => {
|
||||
const profiles = await list(true).catch(handleError)
|
||||
recentInstances.value = Object.values(profiles).sort((a, b) => {
|
||||
return dayjs(b.metadata.last_played ?? 0).diff(dayjs(a.metadata.last_played ?? 0))
|
||||
})
|
||||
const instancesStore = useInstances()
|
||||
const { instancesByPlayed } = storeToRefs(instancesStore)
|
||||
|
||||
const getInstances = async () => {
|
||||
await instancesStore.refreshInstances()
|
||||
|
||||
// filter? TODO: Change this to be reactive along with fetching the rest.
|
||||
let filters = []
|
||||
for (const instance of recentInstances.value) {
|
||||
for (const instance of instancesByPlayed.value) {
|
||||
if (instance.metadata.linked_data && instance.metadata.linked_data.project_id) {
|
||||
filters.push(`NOT"project_id"="${instance.metadata.linked_data.project_id}"`)
|
||||
}
|
||||
@ -84,7 +82,7 @@ const unlistenOffline = await offline_listener(async (b) => {
|
||||
// computed sums of recentInstances, featuredModpacks, featuredMods, treating them as arrays if they are not
|
||||
const total = computed(() => {
|
||||
return (
|
||||
(recentInstances.value?.length ?? 0) +
|
||||
(instancesByPlayed.value?.length ?? 0) +
|
||||
(featuredModpacks.value?.length ?? 0) +
|
||||
(featuredMods.value?.length ?? 0)
|
||||
)
|
||||
@ -104,7 +102,7 @@ onUnmounted(() => {
|
||||
{
|
||||
label: 'Jump back in',
|
||||
route: '/library',
|
||||
instances: recentInstances,
|
||||
instances: instancesByPlayed,
|
||||
downloaded: true,
|
||||
},
|
||||
{
|
||||
|
||||
@ -1,23 +1,23 @@
|
||||
<script setup>
|
||||
import { onUnmounted, ref, shallowRef } from 'vue'
|
||||
import { onUnmounted, ref } from 'vue'
|
||||
import GridDisplay from '@/components/GridDisplay.vue'
|
||||
import { list } from '@/helpers/profile.js'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||
import { offline_listener, profile_listener } from '@/helpers/events.js'
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
import { Button, PlusIcon } from 'omorphia'
|
||||
import InstanceCreationModal from '@/components/ui/InstanceCreationModal.vue'
|
||||
import { NewInstanceImage } from '@/assets/icons'
|
||||
import { isOffline } from '@/helpers/utils'
|
||||
import { useInstances } from '@/store/instances'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
const route = useRoute()
|
||||
const breadcrumbs = useBreadcrumbs()
|
||||
|
||||
breadcrumbs.setRootContext({ name: 'Library', link: route.path })
|
||||
|
||||
const profiles = await list(true).catch(handleError)
|
||||
const instances = shallowRef(Object.values(profiles))
|
||||
const instancesStore = useInstances()
|
||||
const { instanceList } = storeToRefs(instancesStore)
|
||||
|
||||
const offline = ref(await isOffline())
|
||||
const unlistenOffline = await offline_listener((b) => {
|
||||
@ -25,9 +25,9 @@ const unlistenOffline = await offline_listener((b) => {
|
||||
})
|
||||
|
||||
const unlistenProfile = await profile_listener(async () => {
|
||||
const profiles = await list(true).catch(handleError)
|
||||
instances.value = Object.values(profiles)
|
||||
await instancesStore.refreshInstances()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
unlistenProfile()
|
||||
unlistenOffline()
|
||||
@ -35,7 +35,7 @@ onUnmounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<GridDisplay v-if="instances.length > 0" label="Instances" :instances="instances" />
|
||||
<GridDisplay v-if="instanceList.length > 0" label="Instances" :instances="instanceList" />
|
||||
<div v-else class="no-instance">
|
||||
<div class="icon">
|
||||
<NewInstanceImage />
|
||||
|
||||
@ -146,7 +146,7 @@ import {
|
||||
} from '@/helpers/process'
|
||||
import { offline_listener, process_listener, profile_listener } from '@/helpers/events'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ref, onUnmounted } from 'vue'
|
||||
import { ref, onUnmounted, defineProps, watch } from 'vue'
|
||||
import { handleError, useBreadcrumbs, useLoading } from '@/store/state'
|
||||
import { isOffline, showProfileInFolder } from '@/helpers/utils.js'
|
||||
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
||||
@ -154,12 +154,28 @@ import { mixpanel_track } from '@/helpers/mixpanel'
|
||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||
import { useFetch } from '@/helpers/fetch'
|
||||
|
||||
const route = useRoute()
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
})
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
const breadcrumbs = useBreadcrumbs()
|
||||
|
||||
const instance = ref(await get(route.params.id).catch(handleError))
|
||||
const instance = ref(await get(route.params.id || props.id).catch(handleError))
|
||||
|
||||
watch(
|
||||
() => route.params.id,
|
||||
async (id) => {
|
||||
if (!id) return
|
||||
instance.value = await get(id).catch(handleError)
|
||||
}
|
||||
)
|
||||
|
||||
breadcrumbs.setName(
|
||||
'Instance',
|
||||
|
||||
@ -440,10 +440,22 @@ const props = defineProps({
|
||||
return false
|
||||
},
|
||||
},
|
||||
playing: {
|
||||
type: Boolean,
|
||||
default() {
|
||||
return false
|
||||
},
|
||||
},
|
||||
versions: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
installed: {
|
||||
type: Boolean,
|
||||
default() {
|
||||
return true
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const projects = ref([])
|
||||
|
||||
43
theseus_gui/src/store/instances.js
Normal file
43
theseus_gui/src/store/instances.js
Normal file
@ -0,0 +1,43 @@
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
import { list } from '@/helpers/profile.js'
|
||||
import { handleError } from '@/store/notifications'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useInstances = defineStore('instancesStore', () => {
|
||||
const instances = ref({})
|
||||
|
||||
const instanceList = computed(() => {
|
||||
return Object.values(instances.value)
|
||||
})
|
||||
const instancesByPlayed = computed(() => {
|
||||
return instanceList.value.sort((a, b) => {
|
||||
return dayjs(b?.metadata?.last_played ?? 0).diff(dayjs(a?.metadata?.last_played ?? 0))
|
||||
})
|
||||
})
|
||||
|
||||
const setInstances = async () => {
|
||||
try {
|
||||
const p = await list(true)
|
||||
instances.value = p
|
||||
} catch (error) {
|
||||
handleError(error)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await setInstances()
|
||||
})
|
||||
|
||||
const refreshInstances = async () => {
|
||||
await setInstances()
|
||||
}
|
||||
|
||||
return {
|
||||
instanceList,
|
||||
instancesByPlayed,
|
||||
|
||||
refreshInstances,
|
||||
}
|
||||
})
|
||||
Loading…
x
Reference in New Issue
Block a user