-
Notifications
You must be signed in to change notification settings - Fork 208
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WIP: Added preview panel with link handling
TODO: Improve styling
- Loading branch information
Showing
12 changed files
with
623 additions
and
189 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
<template> | ||
<keep-alive> | ||
<div class='row-card is-shadowless' :class="[{'row-card--expanded': isSelected}]" @click="$emit('click')"> | ||
<div class='cursor-pointer'> | ||
<header class='card-header is-shadowless' :id='id'> | ||
<div class='card-header-icon mod-logo' v-if="image !== ''"> | ||
<figure class='image is-48x48 image-parent'> | ||
<img :src='image' alt='Mod Logo' class='image-overlap'/> | ||
<img v-if="$store.state.profile.funkyMode" src='../assets/funky_mode.png' alt='Mod Logo' class='image-overlap'/> | ||
</figure> | ||
</div> | ||
<span ref="title" class='card-header-title'><slot name='title'></slot></span> | ||
<slot name='other-icons'></slot> | ||
</header> | ||
</div> | ||
</div> | ||
</keep-alive> | ||
</template> | ||
|
||
<script lang='ts'> | ||
import Vue from 'vue'; | ||
import { Component, Prop, Watch } from 'vue-property-decorator' | ||
@Component | ||
export default class OnlineRowCard extends Vue { | ||
@Prop() | ||
isSelected: boolean | undefined; | ||
@Prop({default: ''}) | ||
image: string | undefined; | ||
@Prop() | ||
id: string | undefined; | ||
async created() { | ||
await this.$store.dispatch('profile/loadModCardSettings'); | ||
} | ||
} | ||
</script> | ||
|
||
|
||
<style lang="scss" scoped> | ||
.card-header-title { | ||
word-break: break-all; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
<script lang="ts" setup> | ||
import { computed } from 'vue'; | ||
import markdownit from 'markdown-it'; | ||
import { markdownItTable } from 'markdown-it-table'; | ||
import LinkProvider from 'src/providers/components/LinkProvider'; | ||
interface MarkdownRenderProps { | ||
markdown: string; | ||
} | ||
const props = defineProps<MarkdownRenderProps>(); | ||
const markdownToRender = computed(() => { | ||
const md = markdownit("commonmark", {}) as any; | ||
md.use(markdownItTable) | ||
return md.render(props.markdown); | ||
}) | ||
function captureClick(e: Event) { | ||
if (e.target && e.target instanceof HTMLElement) { | ||
if (e.target.tagName.toLowerCase() === "a") { | ||
if (e.target.getAttribute("href").startsWith("#")) { | ||
return e; | ||
} else { | ||
LinkProvider.instance.openLink(e.target.getAttribute("href")!); | ||
} | ||
} | ||
} | ||
} | ||
</script> | ||
|
||
<template> | ||
<p class="markdown-body" v-html="markdownToRender" @click.prevent="captureClick"></p> | ||
</template> | ||
|
||
<style lang="scss" scoped> | ||
@import '~modern-normalize/modern-normalize.css'; | ||
@import "~github-markdown-css/github-markdown.css"; | ||
.markdown-body { | ||
padding: 1rem; | ||
overflow-y: auto; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
<script lang="ts" setup> | ||
import ThunderstoreMod from 'src/model/ThunderstoreMod'; | ||
import { ref, watch } from 'vue'; | ||
import GameManager from 'src/model/game/GameManager'; | ||
import MarkdownRender from 'components/v2/MarkdownRender.vue'; | ||
import { valueToReadableDate } from 'src/utils/DateUtils'; | ||
interface ModPreviewPanelProps { | ||
mod: ThunderstoreMod; | ||
} | ||
const activeGame = GameManager.activeGame; | ||
const props = defineProps<ModPreviewPanelProps>(); | ||
let readme = ref<string | null>(null); | ||
function fetchReadme(mod: ThunderstoreMod) { | ||
readme.value = null; | ||
fetch(`https://thunderstore.dev/api/experimental/package/${mod.getOwner()}/${mod.getName()}/${mod.getLatestVersion()}/readme/`) | ||
.then(res => { | ||
return res; | ||
}) | ||
.then(res => res.json()) | ||
.then(res => { | ||
console.log("README:", res) | ||
return res; | ||
}) | ||
.then(res => readme.value = res.markdown); | ||
} | ||
fetchReadme(props.mod); | ||
watch(() => props.mod, fetchReadme); | ||
function getReadableDate(date: string): string { | ||
return valueToReadableDate(new Date(date)); | ||
} | ||
function getReadableCategories(mod: ThunderstoreMod) { | ||
return mod.getCategories().join(", "); | ||
} | ||
</script> | ||
|
||
<template> | ||
<div class="c-preview-panel"> | ||
<div class="c-preview-panel__container"> | ||
<h1 class="title">{{ mod.getName() }}</h1> | ||
<h2 class="subtitle">By {{ mod.getOwner() }}</h2> | ||
<p class='card-timestamp'><strong>Last updated:</strong> {{getReadableDate(mod.getDateUpdated())}}</p> | ||
<p class='card-timestamp'><strong>Categories:</strong> {{getReadableCategories(mod)}}</p> | ||
<div class="margin-top"> | ||
<p class="description">{{ mod.getDescription() }}</p> | ||
</div> | ||
<div class="sticky-top inherit-background-colour sticky-top--no-shadow sticky-top--opaque no-margin sticky-top--no-padding"> | ||
<div class="pad pad--top"> | ||
<button class="button is-info margin-right">Download</button> | ||
<button class="button">Donate</button> | ||
</div> | ||
<div class="tabs margin-top"> | ||
<ul> | ||
<li class="is-active"><a>README</a></li> | ||
<li><a>CHANGELOG</a></li> | ||
<li><a>Dependencies</a></li> | ||
</ul> | ||
</div> | ||
</div> | ||
<template v-if="readme == null"> | ||
<p>Loading README</p> | ||
</template> | ||
<template v-else-if="readme.trim().length === 0"> | ||
<p>No README content</p> | ||
</template> | ||
<template v-else> | ||
<MarkdownRender :markdown="readme" /> | ||
</template> | ||
</div> | ||
</div> | ||
</template> | ||
|
||
<style lang="scss" scoped> | ||
.c-preview-panel { | ||
position: sticky; | ||
top: 0; | ||
width: min(50vw, 600px); | ||
height: calc(100vh - 0.75rem); | ||
overflow-y: auto; | ||
&__container { | ||
margin: 1rem; | ||
padding: 1rem; | ||
} | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
<template> | ||
<div> | ||
<OnlineRowCard | ||
v-for='(key, index) in pagedModList' :key="`online-${key.getFullName()}-${index}-${settings.getContext().global.expandedCards}`" | ||
:image="getImageUrl(key)" | ||
:id="index" | ||
:is-selected="selectedMod === key" | ||
@click="() => emitCardClick(key)"> | ||
<template v-slot:title> | ||
<div> | ||
<span class="selectable">{{key.getName()}}</span> | ||
<div> | ||
<span class="card-byline">{{key.getOwner()}}</span> | ||
</div> | ||
</div> | ||
</template> | ||
<template v-slot:other-icons> | ||
<span class='card-header-icon' v-if="isModDeprecated(key)"> | ||
<i class='fas fa-exclamation-triangle' v-tooltip.left="'This mod is potentially broken'"></i> | ||
</span> | ||
<span class='card-header-icon' v-if="key.isPinned() && !readOnly"> | ||
<i class='fas fa-map-pin' v-tooltip.left="'Pinned on Thunderstore'"></i> | ||
</span> | ||
<span class='card-header-icon' v-if="key.getDonationLink() && !readOnly"> | ||
<Link :url="key.getDonationLink()" target="external" tag="span"> | ||
<i class='fas fa-heart' v-tooltip.left="'Donate to the mod author'"></i> | ||
</Link> | ||
</span> | ||
<span class='card-header-icon' v-if="isThunderstoreModInstalled(key) && !readOnly"> | ||
<i class='fas fa-check' v-tooltip.left="'Mod already installed'"></i> | ||
</span> | ||
</template> | ||
</OnlineRowCard> | ||
</div> | ||
</template> | ||
|
||
<script lang="ts"> | ||
import { Prop, Vue } from 'vue-property-decorator'; | ||
import Component from 'vue-class-component'; | ||
import ThunderstoreMod from '../../model/ThunderstoreMod'; | ||
import ManagerSettings from '../../r2mm/manager/ManagerSettings'; | ||
import { ExpandableCard, Link } from '../all'; | ||
import DownloadModModal from './DownloadModModal.vue'; | ||
import ManifestV2 from '../../model/ManifestV2'; | ||
import DonateButton from '../../components/buttons/DonateButton.vue'; | ||
import CdnProvider from '../../providers/generic/connection/CdnProvider'; | ||
import { valueToReadableDate } from '../../utils/DateUtils'; | ||
import OnlineRowCard from 'components/OnlineRowCard.vue'; | ||
@Component({ | ||
components: { | ||
OnlineRowCard, | ||
DonateButton, | ||
DownloadModModal, | ||
ExpandableCard, | ||
Link | ||
} | ||
}) | ||
export default class OnlineModListWithPanel extends Vue { | ||
@Prop() | ||
pagedModList!: ThunderstoreMod[]; | ||
@Prop() | ||
selectedMod: ThunderstoreMod | null = null; | ||
@Prop({default: false}) | ||
readOnly!: boolean; | ||
private funkyMode: boolean = false; | ||
get settings(): ManagerSettings { | ||
return this.$store.getters["settings"]; | ||
}; | ||
get localModList(): ManifestV2[] { | ||
return this.$store.state.profile.modList; | ||
} | ||
get deprecationMap(): Map<string, boolean> { | ||
return this.$store.state.tsMods.deprecated; | ||
} | ||
isModDeprecated(mod: ThunderstoreMod) { | ||
return this.deprecationMap.get(mod.getFullName()) || false; | ||
} | ||
isThunderstoreModInstalled(mod: ThunderstoreMod) { | ||
return this.localModList.find((local: ManifestV2) => local.getName() === mod.getFullName()) != undefined; | ||
} | ||
getImageUrl(mod: ThunderstoreMod): string { | ||
return CdnProvider.replaceCdnHost(mod.getIcon()); | ||
} | ||
emitCardClick(mod: ThunderstoreMod) { | ||
this.$emit("selected-mod", mod); | ||
} | ||
async created() { | ||
this.funkyMode = this.settings.getContext().global.funkyModeEnabled; | ||
} | ||
} | ||
</script> |
Oops, something went wrong.