Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 11 additions & 33 deletions src/components/views/LocalModList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,13 @@

<div class="mod-list-content">
<div class="draggable-content">
<draggable v-model='draggableList'
group="local-mods"
handle=".handle"
@start="drag=store.getters['profile/canSortMods']"
@end="drag=false"
:force-fallback="true"
:scroll-sensitivity="100"
item-key="id">
<template #item="{element}">
<local-mod-card
:mod="element" />
<Suspense>
<LocalModDraggableList/>

<template #fallback>
<SkeletonLocalModCard :mod="mod" v-for="mod of visibleModList"/>
</template>
</draggable>
</Suspense>
</div>
</div>

Expand All @@ -32,36 +26,20 @@
</template>

<script lang="ts" setup>
import Draggable from 'vuedraggable';
import R2Error from '../../model/errors/R2Error';
import { ImmutableProfile } from '../../model/Profile';
import AssociatedModsModal from './LocalModList/AssociatedModsModal.vue';
import DisableModModal from './LocalModList/DisableModModal.vue';
import UninstallModModal from './LocalModList/UninstallModModal.vue';
import LocalModCard from './LocalModList/LocalModCard.vue';
import SearchAndSort from './LocalModList/SearchAndSort.vue';
import { getStore } from '../../providers/generic/store/StoreProvider';
import { State } from '../../store';
import { computed } from 'vue';
import { computed, defineAsyncComponent } from 'vue';
import SkeletonLocalModCard from './LocalModList/SkeletonLocalModCard.vue';

const store = getStore<State>();

const profile = computed<ImmutableProfile>(() => store.getters['profile/activeProfile'].asImmutableProfile());
const draggableList = computed({
get() {
return store.getters['profile/visibleModList'];
},
set(newList: string) {
try {
store.dispatch(
'profile/saveModListToDisk',
{mods: newList, profile: profile.value}
);
} catch (e) {
store.commit('error/handleError', R2Error.fromThrownValue(e));
}
}
});
const LocalModDraggableList = defineAsyncComponent(() => import('./LocalModList/LocalModDraggableList.vue'));

const visibleModList = computed(() => store.getters['profile/visibleModList']);
</script>

<style lang="scss" scoped>
Expand Down
10 changes: 5 additions & 5 deletions src/components/views/LocalModList/LocalModCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { LogSeverity } from '../../../providers/ror2/logging/LoggerProvider';
import Dependants from '../../../r2mm/mods/Dependants';
import { valueToReadableDate } from '../../../utils/DateUtils';
import { splitToNameAndVersion } from '../../../utils/DependencyUtils';
import { computed, ref, watch } from 'vue';
import { computed, onMounted, ref, watch } from 'vue';
import { getStore } from '../../../providers/generic/store/StoreProvider';
import { State } from '../../../store';

Expand All @@ -34,7 +34,7 @@ const isLatestVersion = computed(() => store.getters['tsMods/isLatestVersion'](p
const localModList = computed(() => store.state.profile.modList);
const tsMod = computed(() => store.getters['tsMods/tsMod'](props.mod));

function updateDependencies() {
async function updateDependencies() {
if (props.mod.getDependencies().length === 0) {
return;
}
Expand Down Expand Up @@ -167,9 +167,9 @@ function viewAssociatedMods() {
store.commit('openAssociatedModsModal', props.mod);
}

function created() {
onMounted(() => {
updateDependencies();
}
})

// Need to wrap util call in method to allow access from Vue context
function getReadableDate(value: number): string {
Expand Down Expand Up @@ -284,7 +284,7 @@ function dependencyStringToModName(x: string) {
Enable {{disabledDependencies[0].getDisplayName()}}
</a>

<DonateButton :mod="tsMod"/>
<DonateButton v-if="tsMod" :mod="tsMod"/>
</ExpandableCard>
</template>

Expand Down
55 changes: 55 additions & 0 deletions src/components/views/LocalModList/LocalModDraggableList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<template>
<draggable v-model='draggableList'
group="local-mods"
handle=".handle"
@start="drag=store.getters['profile/canSortMods']"
@end="drag=false"
:force-fallback="true"
:scroll-sensitivity="100"
item-key="id">
<template #item="{element}">
<local-mod-card
:mod="element" />
</template>
</draggable>
</template>
<script setup lang="ts">
import Draggable from 'vuedraggable';
import LocalModCard from './LocalModCard.vue';
import { computed, onMounted, ref, watch } from 'vue';
import { getStore } from '../../../providers/generic/store/StoreProvider';
import { State } from '../../../store';
import ManifestV2 from '../../../model/ManifestV2';
import R2Error from '../../../model/errors/R2Error';
import { ImmutableProfile } from '../../../model/Profile';


const store = getStore<State>();

const profile = computed<ImmutableProfile>(() => store.getters['profile/activeProfile'].asImmutableProfile());

// Hack to workaround draggable issue where the VueX update is slightly delayed, which causes a jumping effect.
const internalVisibleList = ref<ManifestV2[]>([]);
const visibleModList = computed(() => store.getters['profile/visibleModList']);
watch(visibleModList, (newList) => internalVisibleList.value = newList);
onMounted(() => {
internalVisibleList.value = store.getters['profile/visibleModList'];
})

const draggableList = computed({
get() {
return internalVisibleList.value;
},
set(newList: ManifestV2[]) {
internalVisibleList.value = newList;
try {
store.dispatch(
'profile/saveModListToDisk',
{mods: newList, profile: profile.value}
);
} catch (e) {
store.commit('error/handleError', R2Error.fromThrownValue(e));
}
}
});
</script>
53 changes: 53 additions & 0 deletions src/components/views/LocalModList/SkeletonLocalModCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<script lang="ts" setup>
import { ExpandableCard } from '../../all';
import ManifestV2 from '../../../model/ManifestV2';

type LocalModCardProps = {
mod: ManifestV2;
}

const props = defineProps<LocalModCardProps>();
</script>

<template>
<ExpandableCard
:description="mod.getDescription()"
:enabled="mod.isEnabled()"
:id="`${mod.getAuthorName()}-${mod.getName()}-${mod.getVersionNumber()}`"
:image="mod.getIcon()">

<template v-slot:title>
<span class="non-selectable">
<span v-if="!mod.isEnabled()"
class="tag is-warning margin-right margin-right--half-width"
v-tooltip.right="'This mod will not be used in-game'">
Disabled
</span>
<span class="card-title selectable">
<component :is="mod.isEnabled() ? 'span' : 'strike'" class="selectable">
{{mod.getDisplayName()}}
<span class="selectable card-byline">
v{{mod.getVersionNumber()}}
</span>
<span :class="`card-byline ${mod.isEnabled() && 'selectable'}`">
by {{mod.getAuthorName()}}
</span>
</component>
</span>
</span>
</template>

<template v-slot:description>
</template>

<!-- Show icon button row even when card is collapsed -->
<template v-slot:other-icons>
</template>
</ExpandableCard>
</template>

<style scoped lang="scss">
.switch {
position: relative;
}
</style>
2 changes: 1 addition & 1 deletion src/components/views/OnlineModList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
Website <i class="fas fa-external-link-alt margin-left margin-left--half-width"></i>
</ExternalLink>
<template v-if="!readOnly">
<DonateButton :mod="key"/>
<DonateButton v-if="key" :mod="key"/>
</template>
<div class='card-footer-item non-selectable'>
<span><i class='fas fa-download'/> {{key.getDownloadCount()}}</span>
Expand Down
11 changes: 9 additions & 2 deletions src/r2mm/mods/ProfileModList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export default class ProfileModList {
const parsedYaml: any = parseYaml(fileContent) || [];
for(let modIndex in parsedYaml){
const mod = new ManifestV2().fromJsObject(parsedYaml[modIndex]);
await this.setIconPath(mod, profile);
this.setIconPath(mod, profile);
parsedYaml[modIndex] = mod;
}
return parsedYaml;
Expand All @@ -71,7 +71,14 @@ export default class ProfileModList {
public static async saveModList(profile: ImmutableProfile, modList: ManifestV2[]): Promise<R2Error | null> {
const fs = FsProvider.instance;
try {
const yamlModList: string = stringifyYaml(modList);
const yamlModList: string = stringifyYaml(modList, {
replacer: (key, value) => {
if (key === 'icon') {
return undefined;
}
return value;
}
});
try {
await fs.writeFile(
profile.joinToProfilePath('mods.yml'),
Expand Down