aboutsummaryrefslogtreecommitdiff
path: root/src-vue/src/components
diff options
context:
space:
mode:
authorGeckoEidechse <gecko.eidechse+git@pm.me>2024-12-22 23:55:52 +0100
committerGeckoEidechse <gecko.eidechse+git@pm.me>2024-12-22 23:55:52 +0100
commitf1dee718da95836ffa5c0985c9e8f5643e0f3f6f (patch)
tree24967a28bcae1fc1e5b08da9f58bcc678ed52937 /src-vue/src/components
parentcc5ae684221d3165479d7a68556a2bb6fa81cf3a (diff)
downloadFlightCore-f1dee718da95836ffa5c0985c9e8f5643e0f3f6f.tar.gz
FlightCore-f1dee718da95836ffa5c0985c9e8f5643e0f3f6f.zip
dev: Replace with sample Tauri 2.0 project
as a first step to convert FlightCore to Tauri 2.0
Diffstat (limited to 'src-vue/src/components')
-rw-r--r--src-vue/src/components/InstallProgressBar.vue102
-rw-r--r--src-vue/src/components/LanguageSelector.vue75
-rw-r--r--src-vue/src/components/LocalModCard.vue120
-rw-r--r--src-vue/src/components/ModsMenu.vue141
-rw-r--r--src-vue/src/components/NotificationButton.vue81
-rw-r--r--src-vue/src/components/PlayButton.vue157
-rw-r--r--src-vue/src/components/PullRequestsSelector.vue181
-rw-r--r--src-vue/src/components/ThunderstoreModCard.vue319
8 files changed, 0 insertions, 1176 deletions
diff --git a/src-vue/src/components/InstallProgressBar.vue b/src-vue/src/components/InstallProgressBar.vue
deleted file mode 100644
index 21901ac0..00000000
--- a/src-vue/src/components/InstallProgressBar.vue
+++ /dev/null
@@ -1,102 +0,0 @@
-<script lang="ts">
-import { defineComponent } from 'vue';
-import { appWindow } from '@tauri-apps/api/window';
-import { InstallProgress } from '../../../src-tauri/bindings/InstallProgress';
-
-export default defineComponent({
- name: 'InstallProgressBar',
- computed: {
- progressBarStyle(): string {
- return !this.install_or_update ? 'hide-progress' : '';
- }
- },
- data() {
- return {
- percentage: 0,
- color: '#409EFF',
- install_or_update: false,
- status: "unknown",
- current_downloaded: -1,
- total_size: -1,
- };
- },
- methods: {
- formatBytes(bytes: number, decimals = 2) {
- if (bytes === 0) return '0 Bytes';
- const k = 1000;
- const dm = decimals < 0 ? 0 : decimals;
- const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
- const i = Math.floor(Math.log(bytes) / Math.log(k));
- return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
- },
- formatText() {
- if (this.status == "Downloading") {
- const current_downloaded_string = this.formatBytes(this.current_downloaded);
- const total_size_string = this.formatBytes(this.total_size);
- const status = this.$t("generic.downloading");
- return `${status}: ${current_downloaded_string}/${total_size_string}`;
- }
- if (this.status == "Extracting") {
- return this.$t("generic.extracting");
- }
- return "Inactive"; // Needed to keep same size format when progress bar is hidden
- }
- },
- mounted() {
- appWindow.listen<InstallProgress>(
- 'northstar-install-download-progress',
- ({ event, payload }) => {
- this.install_or_update = true;
- let progress = payload;
- this.status = progress.state;
- if (progress.state == "Downloading") {
- this.percentage = ((Number(progress.current_downloaded) / Number(progress.total_size)) * 100);
- this.color = '#409EFF';
- this.current_downloaded = Number(progress.current_downloaded);
- this.total_size = Number(progress.total_size);
- }
- if (progress.state == "Extracting") {
- this.percentage = 100;
- this.color = '#67C23A';
- }
- if (progress.state == "Done") {
- // Clear state again
- this.install_or_update = false
- }
- }
- );
- }
-});
-</script>
-
-<template>
- <el-progress
- :class="progressBarStyle"
- :format="formatText"
- :percentage="percentage"
- :color="color"
- :indeterminate="status === 'Extracting'"
- :duration="1"
- >
- </el-progress>
-</template>
-
-<style scoped>
-.el-progress {
- margin-top: 10px;
-}
-
-/* Set progress bar width */
-.el-progress:deep(.el-progress-bar) {
- width: 200px;
- flex-grow: initial;
-}
-
-.el-progress:deep(.el-progress__text) {
- line-height: 1.2;
-}
-
-.hide-progress {
- opacity: 0;
-}
-</style>
diff --git a/src-vue/src/components/LanguageSelector.vue b/src-vue/src/components/LanguageSelector.vue
deleted file mode 100644
index 0c47e674..00000000
--- a/src-vue/src/components/LanguageSelector.vue
+++ /dev/null
@@ -1,75 +0,0 @@
-<template>
- <el-select v-model="value" class="m-2"
- :placeholder="$t('settings.language_select')" size="large"
- @change="onChange"
- >
- <el-option
- v-for="item in options"
- :key="item.value"
- :label="item.label"
- :value="item.value"
- />
- </el-select>
-</template>
-
-<script lang="ts">
-import { defineComponent } from 'vue';
-import { Store } from 'tauri-plugin-store-api';
-const persistentStore = new Store('flight-core-settings.json');
-
-export default defineComponent({
- name: 'LanguageSelector',
- data: () => ({
- value: '',
- options: [
- {
- value: 'en',
- label: 'English'
- },
- {
- value: 'fr',
- label: 'Français'
- },
- {
- value: 'de',
- label: 'Deutsch'
- },
- {
- value: 'es',
- label: 'Español'
- },
- {
- value: 'pl',
- label: 'polski'
- },
- {
- value: 'ru',
- label: 'русский'
- },
- {
- value: 'it',
- label: 'Italiano'
- },
- {
- value: 'da',
- label: 'Dansk'
- },
- {
- value: 'zh_Hans',
- label: '简体中文'
- },
- ]
- }),
- mounted: async function () {
- const lang: string = await persistentStore.get('lang') as string;
- this.value = lang;
- },
- methods: {
- async onChange(value: string) {
- this.$root!.$i18n.locale = value;
- persistentStore.set('lang', value);
- await persistentStore.save();
- }
- }
-})
-</script>
diff --git a/src-vue/src/components/LocalModCard.vue b/src-vue/src/components/LocalModCard.vue
deleted file mode 100644
index 697cced1..00000000
--- a/src-vue/src/components/LocalModCard.vue
+++ /dev/null
@@ -1,120 +0,0 @@
-<template>
- <el-card shadow="hover">
- <div class="name">
- {{ mod.name }} <span v-if="mod.version != null">(v{{ mod.version }})</span>
- <img
- v-if="mod.thunderstore_mod_string != null"
- :title="$t('mods.local.part_of_ts_mod') + '\n' + mod.thunderstore_mod_string"
- src="/src/assets/thunderstore-icon.png"
- class="image"
- height="16"
- />
- </div>
- <div>
- <el-switch
- style="--el-switch-on-color: #13ce66; --el-switch-off-color: #8957e5"
- v-model="mod.enabled"
- :before-change="() => updateWhichModsEnabled(mod)"
- :loading="global_load_indicator"
- class="switch"
- />
- <el-popconfirm
- :title="$t('mods.local.delete_confirm')"
- :confirm-button-text="$t('generic.yes')"
- :cancel-button-text="$t('generic.no')"
- @confirm="deleteMod(mod)"
- >
- <template #reference>
- <el-button type="danger">
- {{ $t('mods.local.delete') }}
- </el-button>
- </template>
- </el-popconfirm>
- </div>
- </el-card>
-</template>
-
-<script lang="ts">
-import { defineComponent } from "vue";
-import { invoke } from "@tauri-apps/api";
-import { NorthstarMod } from "../../../src-tauri/bindings/NorthstarMod";
-import { showErrorNotification, showNotification } from "../utils/ui";
-
-export default defineComponent({
- name: "LocalModCard",
- props: {
- mod: {
- required: true,
- type: Object as () => NorthstarMod
- }
- },
- data() {
- return {
- global_load_indicator: false,
- };
- },
- methods: {
- async updateWhichModsEnabled(mod: NorthstarMod) {
- this.global_load_indicator = true;
-
- // enable/disable specific mod
- try {
- await invoke("set_mod_enabled_status", {
- gameInstall: this.$store.state.game_install,
- modName: mod.name,
- // Need to set it to the opposite of current state,
- // as current state is only updated after command is run
- isEnabled: !mod.enabled,
- })
- }
- catch (error) {
- showErrorNotification(`${error}`);
- this.global_load_indicator = false;
- return false;
- }
-
- this.global_load_indicator = false;
- return true;
- },
- async deleteMod(mod: NorthstarMod) {
- await invoke("delete_northstar_mod", { gameInstall: this.$store.state.game_install, nsmodName: mod.name })
- .then((message) => {
- // Just a visual indicator that it worked
- showNotification(this.$t('mods.local.success_deleting', { modName: mod.name }));
- })
- .catch((error) => {
- showErrorNotification(error);
- })
- .finally(() => {
- this.$store.commit('loadInstalledMods');
- });
- },
- }
-});
-</script>
-
-<style scoped>
- /*
- This is a hack to style the card body
- since it doesn't work with scoped styles
- */
- :deep(.el-card__body) {
- display: flex !important;
- align-items: center;
- width: 100%;
- justify-content: space-between;
- }
-
- .name {
- display: flex;
- }
-
- .image {
- margin: 0 5px;
- }
-
- .switch {
- padding-left: 5px;
- padding-right: 5px;
- }
-</style>
diff --git a/src-vue/src/components/ModsMenu.vue b/src-vue/src/components/ModsMenu.vue
deleted file mode 100644
index 66ecc71a..00000000
--- a/src-vue/src/components/ModsMenu.vue
+++ /dev/null
@@ -1,141 +0,0 @@
-<template>
- <nav class="fc_mods__menu">
- <el-menu
- default-active="1"
- text-color="#fff"
- >
- <h5>{{ $t('menu.mods') }}</h5>
- <el-menu-item index="1" @click="$emit('showLocalMods', true)">
- <el-icon><Folder /></el-icon>
- <span>{{ $t('mods.menu.local') }}</span>
- </el-menu-item>
-
- <!-- Display a badge if there are some outdated Thunderstore mods -->
- <el-menu-item v-if="outdatedThunderstoreModsCount !== 0" index="2" @click="$emit('showLocalMods', false)">
- <el-badge :value="outdatedThunderstoreModsCount" class="item" type="warning">
- <el-icon><Connection /></el-icon>
- <span>{{ $t('mods.menu.online') }}</span>
- </el-badge>
- </el-menu-item>
- <el-menu-item v-else index="2" @click="$emit('showLocalMods', false)">
- <el-icon><Connection /></el-icon>
- <span>{{ $t('mods.menu.online') }}</span>
- </el-menu-item>
-
- <!-- Search inputs -->
- <h5>{{ $t('mods.menu.filter') }}</h5>
- <el-input v-model="$store.state.search.searchValue" :placeholder="$t('mods.menu.search')" clearable />
- <el-select
- v-if="!showingLocalMods"
- v-model="$store.state.search.sortValue"
- :placeholder="$t('mods.menu.sort_mods')"
- >
- <el-option
- v-for="item of sortValues"
- :key="item.value"
- :label="item.label"
- :value="item.value"
- />
- </el-select>
- <el-select
- v-if="!showingLocalMods"
- v-model="$store.state.search.selectedCategories"
- multiple
- :placeholder="$t('mods.menu.select_categories')"
- >
- <el-option
- v-for="item in $store.state.thunderstoreModsCategories"
- :key="item"
- :label="item"
- :value="item"
- />
- </el-select>
-
- </el-menu>
- </nav>
-</template>
-
-<script lang="ts">
-import { defineComponent } from 'vue'
-import { SortOptions } from '../utils/SortOptions.d';
-import { isThunderstoreModOutdated } from "../utils/thunderstore/version";
-import { ThunderstoreMod } from "../../../src-tauri/bindings/ThunderstoreMod";
-
-export default defineComponent({
- name: 'ModsMenu',
- props: {
- showingLocalMods: {
- required: true,
- type: Boolean
- }
- },
- mounted() {
- this.$store.state.search.sortValue = this.sortValues[3].value;
- },
- computed: {
- outdatedThunderstoreModsCount(): number {
- return this.$store.state.thunderstoreMods
- .filter((mod: ThunderstoreMod) => isThunderstoreModOutdated(mod))
- .length;
- },
- sortValues(): { label: string, value: string }[] {
- return Object.keys(SortOptions).map((key: string) => ({
- value: key,
- label: this.$t('mods.menu.sort.' + Object.values(SortOptions)[Object.keys(SortOptions).indexOf(key)])
- }));
- }
- }
-})
-</script>
-
-<style scoped>
-.fc_mods__menu {
- display: flex;
- max-width: 222px;
- min-width: 222px;
- padding: 10px;
-}
-
-.fc_mods__menu h5 {
- margin: 8px 0 16px 5px;
-}
-
-.fc_mods__menu h5:not(:first-child) {
- margin-top: 32px;
-}
-
-.fc_mods__menu > .el-menu {
- background-color: transparent;
- border: none;
- width: 100%;
-}
-
-.fc_mods__menu > .el-menu > .el-menu-item {
- height: 32px;
- margin-bottom: 5px;
- border-radius: 5px;
- color: #e2e6e7;
-}
-
-.fc_mods__menu > .el-menu > .el-menu-item:hover {
- background-color: #4e4e4e3b;
-}
-
-.fc_mods__menu > .el-menu > .el-menu-item.is-active {
- color: white;
- background-color: #4e4e4e7a;
-}
-
-.el-select {
- width: 100%;
- margin-top: 5px;
-}
-
-/* Outdated thunderstore mods count */
-.el-badge {
- width: 100%;
-}
-.el-badge:deep(.el-badge__content) {
- top: 28px !important;
-}
-</style>
diff --git a/src-vue/src/components/NotificationButton.vue b/src-vue/src/components/NotificationButton.vue
deleted file mode 100644
index 3835032d..00000000
--- a/src-vue/src/components/NotificationButton.vue
+++ /dev/null
@@ -1,81 +0,0 @@
-<template>
- <el-dropdown trigger="click" placement="bottom-end" max-height="280" popper-class="fc_popper">
- <el-badge v-if="notifications.length != 0" :value="notifications.length" :max="9" class="item" type="primary">
- <el-button color="white" icon="BellFilled" circle />
- </el-badge>
- <el-button v-else color="white" icon="BellFilled" circle />
- <template #dropdown>
- <el-dropdown-menu :key="counter">
- <el-alert
- v-if="notifications.length != 0"
- v-for="(notification, i) in notifications"
- :key="i"
- :title="notification.title"
- :description="notification.text"
- :type="notification.type"
- show-icon
- style="width: 300px"
- @close.stop="removeNotification(i)"
- />
- <el-result
- v-else
- icon="success"
- :title="i18n.global.tc('notification.no_new.title')"
- :sub-title="i18n.global.tc('notification.no_new.text')"
- >
- <template #icon>
- </template>
- </el-result>
- </el-dropdown-menu>
- </template>
- </el-dropdown>
-</template>
-
-<script lang="ts">
-import { defineComponent } from 'vue';
-import {Notification} from '../plugins/modules/notifications';
-import {i18n} from "../main";
-
-export default defineComponent({
- name: 'NotificationButton',
- data: () => ({
- // This variable is used as a key for the dropdown menu, so we can force it to refresh when a item is deleted.
- counter: 0
- }),
- computed: {
- i18n() {
- return i18n
- },
- notifications(): Notification[] {
- return this.$store.state.notifications.notifications;
- }
- },
- methods: {
- removeNotification(index: number) {
- this.$store.commit('removeNotification', index);
- // By refreshing the notifications list, we ensure the first notification get the index 0, ensuring this list
- // is synchronized with the store list.
- this.counter += 1;
- }
- }
-})
-</script>
-
-<style scoped>
-.el-dropdown {
- height: 100%;
- max-height: 300px;
-}
-
-.el-button {
- margin: auto 25px auto 0 !important;
-}
-
-.el-alert {
- margin: 5px 10px 5px 5px;
-}
-
-.el-badge:deep(sup) {
- transform: translate(-10px, 5px) !important;
-}
-</style>
diff --git a/src-vue/src/components/PlayButton.vue b/src-vue/src/components/PlayButton.vue
deleted file mode 100644
index 57b408c2..00000000
--- a/src-vue/src/components/PlayButton.vue
+++ /dev/null
@@ -1,157 +0,0 @@
-<script lang="ts">
-import { defineComponent } from 'vue';
-import { NorthstarState } from '../utils/NorthstarState';
-import { ReleaseCanal } from '../utils/ReleaseCanal';
-
-export default defineComponent({
- name: 'PlayButton',
- computed: {
- currentCanal: {
- get(): ReleaseCanal {
- return this.$store.state.northstar_release_canal;
- },
- set(value: ReleaseCanal) {
- if (value !== this.currentCanal) {
- this.$store.commit('toggleReleaseCandidate');
- }
- }
- },
- playButtonLabel(): string {
- if (this.$store.state.northstar_is_running) {
- return this.$t("play.button.northstar_is_running");
- }
-
- switch (this.$store.state.northstar_state) {
- case NorthstarState.GAME_NOT_FOUND:
- return this.$t("play.button.select_game_dir");
- case NorthstarState.INSTALL:
- return this.$t("play.button.install");
- case NorthstarState.INSTALLING:
- return this.$t("play.button.installing");
- case NorthstarState.MUST_UPDATE:
- return this.$t("play.button.update");
- case NorthstarState.UPDATING:
- return this.$t("play.button.updating");
- case NorthstarState.READY_TO_PLAY:
- return this.$t("play.button.ready_to_play");
-
- default:
- return "";
- }
- },
- northstarIsRunning(): boolean {
- return this.$store.state.northstar_is_running;
- },
- options(): { key: string, value: string }[] {
- return Object.keys(ReleaseCanal).map(function (v) {
- return {
- key: v,
- value: Object.keys(ReleaseCanal)[Object.values(ReleaseCanal).indexOf(v)]
- }
- });
- },
- selectOptions(): { label: string, options: { value: ReleaseCanal, label: string }[] }[] {
- return [
- {
- label: 'Beta',
- options: [
- {
- value: ReleaseCanal.RELEASE_CANDIDATE,
- label: this.$t('channels.names.NorthstarReleaseCandidate'),
- },
- ]
- },
- {
- label: 'Stable',
- options: [
- {
- value: ReleaseCanal.RELEASE,
- label: 'Northstar',
- },
- ]
- }
- ];
- },
- showReleaseSwitch(): boolean {
- return this.$store.state.enableReleasesSwitch;
- },
-
- /**
- * Button has rounded edges on its right only if releases switching is enabled.
- */
- buttonRadiusStyle(): string {
- return this.showReleaseSwitch
- ? 'border-radius: 2px 0 0 2px;'
- : 'border-radius: 2px';
- },
- },
- methods: {
- async launchGame() {
- this.$store.commit('launchGame');
- }
- }
-});
-</script>
-
-<template>
- <nav>
- <el-button :disabled="northstarIsRunning"
- type="primary" size="large" @click="launchGame"
- class="fc_launch__button" :style="buttonRadiusStyle">
- {{ playButtonLabel }}
- </el-button>
- <el-select v-if="showReleaseSwitch" :disabled="northstarIsRunning"
- v-model="currentCanal" placeholder="Select">
- <el-option-group
- v-for="group in selectOptions"
- :key="group.label"
- :label="group.label"
- >
- <el-option
- v-for="item in group.options"
- :key="item.value"
- :label="item.label"
- :value="item.value"
- />
- </el-option-group>
- </el-select>
- </nav>
-</template>
-
-<style scoped>
-nav {
- margin-right: 20px;
-}
-
-button {
- text-transform: uppercase;
- padding: 30px;
- font-size: 15px;
- margin-right: 0;
-}
-
-.fc_launch__button:focus {
- background-color: var(--el-color-primary);
- border-color: var(--el-color-primary);
-}
-
-/* Release canal selector */
-.el-select {
- width: 31px;
- border-left: 1px solid rgb(176, 205, 255);
-}
-
-.el-select:deep(.el-select__wrapper) {
- padding: 0 9px 0 0;
- background-color: var(--el-color-primary);
- border: none;
- border-radius: 0 2px 2px 0;
- height: 62px;
- box-shadow: none !important;
- --el-disabled-bg-color: #a0cfff;
-}
-
-.el-select:deep(.el-icon) {
- color: white !important;
-}
-</style>
diff --git a/src-vue/src/components/PullRequestsSelector.vue b/src-vue/src/components/PullRequestsSelector.vue
deleted file mode 100644
index bd17ed14..00000000
--- a/src-vue/src/components/PullRequestsSelector.vue
+++ /dev/null
@@ -1,181 +0,0 @@
-<template>
- <div>
- <el-collapse @change="onChange">
- <el-collapse-item name="1" @keydown.space="launcherSearchSpace">
- <template #title>
- Launcher PRs
- <el-input class="pr_search_input" v-model="launcherSearch" placeholder="Filter pull requests" @click.stop="() => false"></el-input>
- </template>
-
- <p v-if="pull_requests_launcher.length === 0">
- <el-progress
- :show-text="false"
- :percentage="100"
- status="warning"
- :indeterminate="true"
- :duration="1"
- style="margin: 15px"
- />
- </p>
- <el-card
- v-else-if="filtered_launcher_pull_requests.length !== 0"
- shadow="hover"
- v-for="pull_request in filtered_launcher_pull_requests"
- v-bind:key="pull_request.url"
- >
- <el-button type="primary" @click="installLauncherPR(pull_request)">Install</el-button>
- <el-button type="primary" @click="downloadLauncherPR(pull_request)">Download</el-button>
- <a target="_blank" :href="pull_request.html_url">
- {{ pull_request.number }}: {{ pull_request.title }}
- </a>
- <el-tag v-for="label in pull_request.labels">{{ label }}</el-tag>
- </el-card>
- <div v-else class="no_matching_pr">
- No matching PR found.
- </div>
- </el-collapse-item>
-
- <el-collapse-item name="2" @keydown.space="modsSearchSpace">
- <template #title>
- Mods PRs
- <el-input class="pr_search_input" v-model="modsSearch" placeholder="Filter pull requests" @click.stop="() => false"></el-input>
- </template>
- <div style="margin: 15px">
- <el-alert title="Warning" type="warning" :closable="false" show-icon>
- Mod PRs are installed into a separate profile. Make sure to launch via
- 'r2ns-launch-mod-pr-version.bat' or via '-profile=R2Northstar-PR-test-managed-folder' to actually
- run the PR version!
- </el-alert>
- </div>
- <p v-if="pull_requests_mods.length === 0">
- <el-progress
- :show-text="false"
- :percentage="100"
- status="warning"
- :indeterminate="true"
- :duration="1"
- style="margin: 15px"
- />
- </p>
- <el-card
- v-else-if="filtered_mods_pull_requests.length !== 0"
- shadow="hover"
- v-for="pull_request in filtered_mods_pull_requests"
- v-bind:key="pull_request.url"
- >
- <el-button type="primary" @click="installModsPR(pull_request)">Install</el-button>
- <el-button type="primary" @click="downloadModsPR(pull_request)">Download</el-button>
- <a target="_blank" :href="pull_request.html_url">
- {{ pull_request.number }}: {{ pull_request.title }}
- </a>
- <el-tag v-for="label in pull_request.labels">{{ label }}</el-tag>
- </el-card>
- <div v-else class="no_matching_pr">
- No matching PR found.
- </div>
- </el-collapse-item>
- </el-collapse>
- </div>
-</template>
-
-<script lang="ts">
-import { defineComponent } from 'vue'
-import { PullRequestType } from '../../../src-tauri/bindings/PullRequestType';
-import { PullsApiResponseElement } from '../../../src-tauri/bindings/PullsApiResponseElement';
-import { fuzzy_filter } from "../utils/filter";
-
-export default defineComponent({
- name: 'PullRequestsSelector',
- data: () => ({
- launcherSearch: '',
- modsSearch: ''
- }),
- computed: {
- pull_requests_launcher(): PullsApiResponseElement[] {
- return this.$store.state.pullrequests.pull_requests_launcher;
- },
- pull_requests_mods(): PullsApiResponseElement[] {
- return this.$store.state.pullrequests.pull_requests_mods;
- },
-
- filtered_launcher_pull_requests(): PullsApiResponseElement[] {
- if (this.launcherSearch.length === 0) {
- return this.pull_requests_launcher;
- }
-
- return this.pull_requests_launcher.filter(pr =>
- // Check PR id and title
- fuzzy_filter(pr.number.toString(), this.launcherSearch) || fuzzy_filter(pr.title, this.launcherSearch));
- },
- filtered_mods_pull_requests(): PullsApiResponseElement[] {
- if (this.modsSearch.length === 0) {
- return this.pull_requests_mods;
- }
-
- return this.pull_requests_mods.filter(pr =>
- // Check PR id and title
- fuzzy_filter(pr.number.toString(), this.modsSearch) || fuzzy_filter(pr.title, this.modsSearch));
- },
- },
- methods: {
- onChange(e: Object) {
- const openedCollapseNames = Object.values(e);
- if (openedCollapseNames.includes('1') && this.pull_requests_launcher.length === 0) {
- this.getPullRequests('Launcher');
- }
- if (openedCollapseNames.includes('2') && this.pull_requests_mods.length === 0) {
- this.getPullRequests('Mods');
- }
- },
- launcherSearchSpace(e: KeyboardEvent) {
- e.preventDefault();
- this.launcherSearch += ' ';
- },
- modsSearchSpace(e: KeyboardEvent) {
- e.preventDefault();
- this.modsSearch += ' ';
- },
- async getPullRequests(pull_request_type: PullRequestType) {
- this.$store.commit('getPullRequests', pull_request_type);
- },
- async downloadLauncherPR(pull_request: PullsApiResponseElement) {
- this.$store.commit('downloadLauncherPR', pull_request);
- },
- async downloadModsPR(pull_request: PullsApiResponseElement) {
- this.$store.commit('downloadModsPR', pull_request);
- },
- async installLauncherPR(pull_request: PullsApiResponseElement) {
- this.$store.commit('installLauncherPR', pull_request);
- },
- async installModsPR(pull_request: PullsApiResponseElement) {
- this.$store.commit('installModsPR', pull_request);
- },
- }
-})
-</script>
-
-<style scoped>
-.el-collapse {
- border-radius: var(--el-border-radius-base);
- overflow: hidden;
-}
-
-:deep(.el-collapse-item__header) {
- padding-left: 10px;
- font-size: 14px;
-}
-
-.el-collapse:deep(.el-collapse-item__arrow) {
- margin: 0 8px;
-}
-
-.pr_search_input {
- width: 200px;
- margin: 0 0 0 auto;
-}
-
-.no_matching_pr {
- margin: 0 auto;
- width: max-content;
-}
-</style>
diff --git a/src-vue/src/components/ThunderstoreModCard.vue b/src-vue/src/components/ThunderstoreModCard.vue
deleted file mode 100644
index 11be7545..00000000
--- a/src-vue/src/components/ThunderstoreModCard.vue
+++ /dev/null
@@ -1,319 +0,0 @@
-<template>
- <el-card :body-style="getBodyStyle" :style="getCardStyle">
- <img
- :src="latestVersion.icon"
- class="image"
- />
- <div style="padding: 0 10px 10px;">
- <span class="statContainer">
- <el-icon class="no-inherit">
- <Download />
- </el-icon>
- {{ modDownloadsCount }}
- </span>
-
- <span class="statContainer">
- {{ mod.rating_score }}
- <el-icon class="no-inherit">
- <Star />
- </el-icon>
- </span>
- <br />
-
- <div class="name hide-text-overflow">{{ mod.name }}</div>
- <div class="author hide-text-overflow">{{ $t('mods.card.by') }} {{ mod.owner }}</div>
- <div class="desc">
- {{ latestVersion.description }}
- </div>
-
- <span style="display: flex">
- <el-button
- :type="modButtonType"
- style="flex: 6"
- :loading="isBeingInstalled || isBeingUpdated"
- @click.stop="installMod(mod)"
- >
- {{ $t(modButtonText) }}
- </el-button>
-
- <!-- Information dropdown menu -->
- <el-button v-if="!modIsRemovable"
- link type="info" class="infoBtn" @click="openURL(mod.package_url)">
- <el-icon>
- <InfoFilled />
- </el-icon>
- </el-button>
-
- <el-dropdown v-else>
- <el-icon class="infoBtn moreBtn">
- <MoreFilled />
- </el-icon>
- <template #dropdown>
- <el-dropdown-menu>
- <el-dropdown-item @click="openURL(mod.package_url)">
- {{ $t('mods.card.more_info') }}
- </el-dropdown-item>
- <el-dropdown-item @click="deleteMod(mod)">
- {{ $t('mods.card.remove') }}
- </el-dropdown-item>
- </el-dropdown-menu>
- </template>
- </el-dropdown>
- </span>
- </div>
- </el-card>
-</template>
-
-<script lang="ts">
-import { defineComponent } from "vue";
-import { ThunderstoreMod } from "../../../src-tauri/bindings/ThunderstoreMod";
-import { ThunderstoreModVersion } from "../../../src-tauri/bindings/ThunderstoreModVersion";
-import { invoke, shell } from "@tauri-apps/api";
-import { ThunderstoreModStatus } from "../utils/thunderstore/ThunderstoreModStatus";
-import { NorthstarMod } from "../../../src-tauri/bindings/NorthstarMod";
-import { NorthstarState } from "../utils/NorthstarState";
-import { ElMessageBox } from "element-plus";
-import { showErrorNotification, showNotification } from "../utils/ui";
-
-export default defineComponent({
- name: "ThunderstoreModCard",
- props: {
- mod: {
- required: true,
- type: Object as () => ThunderstoreMod
- }
- },
- data: () => ({
- isBeingInstalled: false,
- isBeingUpdated: false
- }),
- computed: {
- getBodyStyle(): Object {
- return this.mod.is_deprecated ? { 'background-color': 'rgba(255, 0, 0, 0.42)' } : {};
- },
-
- getCardStyle(): Object {
- return this.mod.is_deprecated ? { 'border': '1px solid red' } : {};
- },
-
- latestVersion(): ThunderstoreModVersion {
- return this.mod.versions[0];
- },
-
- /**
- * Returns the status of a given mod.
- */
- modStatus(): ThunderstoreModStatus {
- if (this.isBeingInstalled) {
- return ThunderstoreModStatus.BEING_INSTALLED;
- }
- if (this.isBeingUpdated) {
- return ThunderstoreModStatus.BEING_UPDATED;
- }
-
- // Ensure mod is up-to-date.
- const tsModPrefix = this.getThunderstoreDependencyStringPrefix(this.latestVersion.full_name);
- const matchingMods: NorthstarMod[] = this.$store.state.installed_mods.filter((mod: NorthstarMod) => {
- if (!mod.thunderstore_mod_string) return false;
- return this.getThunderstoreDependencyStringPrefix(mod.thunderstore_mod_string!) === tsModPrefix;
- });
- if (matchingMods.length !== 0) {
- // There shouldn't be several mods with same dependency string, but we never know...
- const matchingMod = matchingMods[0];
- // A mod is outdated if its dependency strings differs from Thunderstore dependency string
- // (no need for semver check here)
- return matchingMod.thunderstore_mod_string === this.latestVersion.full_name
- ? ThunderstoreModStatus.INSTALLED
- : ThunderstoreModStatus.OUTDATED;
- }
-
- return ThunderstoreModStatus.NOT_INSTALLED;
- },
-
- /**
- * Returns button text associated to a mod.
- */
- modButtonText(): string {
- switch (this.modStatus) {
- case ThunderstoreModStatus.BEING_INSTALLED:
- return "mods.card.button.being_installed";
- case ThunderstoreModStatus.BEING_UPDATED:
- return "mods.card.button.being_updated";
- case ThunderstoreModStatus.INSTALLED:
- return "mods.card.button.installed";
- case ThunderstoreModStatus.NOT_INSTALLED:
- return "mods.card.button.install";
- case ThunderstoreModStatus.OUTDATED:
- return "mods.card.button.outdated";
- }
- },
-
- /**
- * Returns button type associated to a mod.
- */
- modButtonType(): string {
- switch (this.modStatus) {
- case ThunderstoreModStatus.BEING_INSTALLED:
- return "primary";
- case ThunderstoreModStatus.INSTALLED:
- return "success";
- case ThunderstoreModStatus.NOT_INSTALLED:
- return "primary";
- case ThunderstoreModStatus.OUTDATED:
- case ThunderstoreModStatus.BEING_UPDATED:
- return "warning";
- }
- },
-
- /**
- * Tells if a Thunderstore mod can be removed.
- * This is used to tell if we should display the "Remove mod" option.
- **/
- modIsRemovable(): boolean {
- return [ThunderstoreModStatus.INSTALLED, ThunderstoreModStatus.OUTDATED]
- .includes(this.modStatus);
- },
-
- /**
- * This computes the total count of downloads of a given mod, by adding
- * download count of each of its releases.
- */
- modDownloadsCount(): number {
- let totalDownloads = 0;
- this.mod.versions.map((version: ThunderstoreModVersion) => totalDownloads += version.downloads);
- return totalDownloads;
- },
- },
- methods: {
- /**
- * This opens an URL in user's favorite web browser.
- * This is used to open Thunderstore mod pages.
- */
- openURL(url: string): void {
- shell.open(url);
- },
-
- /**
- * Strips off a Thunderstore dependency string from its version
- * (e.g. "taskinoz-WallrunningTitans-1.0.0" to
- * "taskinoz-WallrunningTitans").
- */
- getThunderstoreDependencyStringPrefix(dependency: string): string {
- const dependencyStringMembers = dependency.split('-');
- return `${dependencyStringMembers[0]}-${dependencyStringMembers[1]}`;
- },
-
- async deleteMod(mod: ThunderstoreMod) {
-
- // Show pop-up to confirm delete
- ElMessageBox.confirm(
- this.$t('mods.card.remove_dialog_text'),
- this.$t('mods.card.remove_dialog_title'),
- {
- confirmButtonText: this.$t('generic.yes'),
- cancelButtonText: this.$t('generic.cancel'),
- type: 'warning',
- }
- )
- .then(async () => { // Deletion confirmed
- await invoke<string>("delete_thunderstore_mod", { gameInstall: this.$store.state.game_install, thunderstoreModString: this.latestVersion.full_name })
- .then((message) => {
- showNotification(this.$t('mods.card.remove_success', { modName: mod.name }), message);
- })
- .catch((error) => {
- showErrorNotification(error);
- })
- .finally(() => {
- this.$store.commit('loadInstalledMods');
- });
- })
- .catch(() => { // Deletion cancelled
- console.log("Deleting Thunderstore mod cancelled.")
- })
- },
-
- async installMod(mod: ThunderstoreMod) {
- // set internal state according to current installation state
- if (this.modStatus === ThunderstoreModStatus.OUTDATED) {
- this.isBeingUpdated = true;
- } else {
- this.isBeingInstalled = true;
- }
-
- // Capture translation method in a context, so it can be used outside Vue component context.
- // (see https://github.com/R2NorthstarTools/FlightCore/issues/384)
- (async (translate: Function) => {
- await invoke<string>("install_mod_wrapper", { gameInstall: this.$store.state.game_install, thunderstoreModString: this.latestVersion.full_name }).then((message) => {
- showNotification(translate('mods.card.install_success', { modName: mod.name }), message);
- })
- .catch((error) => {
- showErrorNotification(error);
- })
- .finally(() => {
- this.isBeingInstalled = false;
- this.isBeingUpdated = false;
- this.$store.commit('loadInstalledMods');
- });
- // @ts-ignore
- })(this.$i18n.t);
-
- },
- }
-});
-</script>
-
-<style scoped>
-.el-card {
- display: inline-block;
- max-width: 178px;
- margin: 5px;
- --el-card-padding: 0;
-}
-
-.deprecated {
- background-color: red !important;
-}
-
-.author {
- font-size: 14px;
- font-style: italic;
-}
-
-.hide-text-overflow {
- white-space: nowrap;
- text-overflow: ellipsis;
- overflow: hidden;
-}
-
-.desc {
- font-size: 12px;
- margin: 8px 0 16px;
- height: 57px;
- text-overflow: ellipsis;
- overflow: hidden;
-}
-
-.statContainer {
- font-size: 14px;
-}
-
-.statContainer:nth-child(2) {
- float: right;
-}
-
-.infoBtn {
- width: 20px;
- padding: 0 !important;
- font-size: 20px;
- border: none;
-}
-
-.moreBtn {
- margin-left: 10px;
- height: auto;
-}
-
-.image {
- background-color: lightgray;
-}
-</style>