v0.9.1 fixes

This commit is contained in:
Jai A
2024-12-24 22:30:10 -07:00
parent 5b00ac17e5
commit 4a031f7bbd
15 changed files with 195 additions and 104 deletions

4
Cargo.lock generated
View File

@@ -8956,7 +8956,7 @@ dependencies = [
[[package]] [[package]]
name = "theseus" name = "theseus"
version = "0.9.0" version = "0.9.1"
dependencies = [ dependencies = [
"async-recursion", "async-recursion",
"async-tungstenite", "async-tungstenite",
@@ -9007,7 +9007,7 @@ dependencies = [
[[package]] [[package]]
name = "theseus_gui" name = "theseus_gui"
version = "0.9.0" version = "0.9.1"
dependencies = [ dependencies = [
"chrono", "chrono",
"cocoa 0.25.0", "cocoa 0.25.0",

View File

@@ -1,7 +1,7 @@
{ {
"name": "@modrinth/app-frontend", "name": "@modrinth/app-frontend",
"private": true, "private": true,
"version": "0.9.0", "version": "0.9.1",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View File

@@ -16,6 +16,7 @@ import {
RestoreIcon, RestoreIcon,
LogOutIcon, LogOutIcon,
RightArrowIcon, RightArrowIcon,
LeftArrowIcon,
} from '@modrinth/assets' } from '@modrinth/assets'
import { Avatar, Button, ButtonStyled, Notifications, OverflowMenu } from '@modrinth/ui' import { Avatar, Button, ButtonStyled, Notifications, OverflowMenu } from '@modrinth/ui'
import { useLoading, useTheming } from '@/store/state' import { useLoading, useTheming } from '@/store/state'
@@ -256,25 +257,19 @@ themeStore.$subscribe(() => {
sidebarToggled.value = !themeStore.toggleSidebar sidebarToggled.value = !themeStore.toggleSidebar
}) })
const forceSidebar = ref(false) const forceSidebar = computed(
() => route.path.startsWith('/browse') || route.path.startsWith('/project'),
)
const sidebarVisible = computed(() => sidebarToggled.value || forceSidebar.value) const sidebarVisible = computed(() => sidebarToggled.value || forceSidebar.value)
const showAd = computed(() => !(!sidebarVisible.value || hasPlus.value)) const showAd = computed(() => !(!sidebarVisible.value || hasPlus.value))
router.afterEach((to) => {
forceSidebar.value = to.path.startsWith('/browse') || to.path.startsWith('/project')
})
const currentTimeout = ref(null)
watch( watch(
showAd, showAd,
() => { () => {
if (!showAd.value) { if (!showAd.value) {
if (currentTimeout.value) clearTimeout(currentTimeout.value)
hide_ads_window(true) hide_ads_window(true)
} else { } else {
currentTimeout.value = setTimeout(() => { init_ads_window(true)
init_ads_window(true)
}, 400)
} }
}, },
{ immediate: true }, { immediate: true },
@@ -443,6 +438,20 @@ function handleAuxClick(e) {
<div data-tauri-drag-region class="app-grid-statusbar bg-bg-raised h-[--top-bar-height] flex"> <div data-tauri-drag-region class="app-grid-statusbar bg-bg-raised h-[--top-bar-height] flex">
<div data-tauri-drag-region class="flex p-3"> <div data-tauri-drag-region class="flex p-3">
<ModrinthAppLogo class="h-full w-auto text-contrast pointer-events-none" /> <ModrinthAppLogo class="h-full w-auto text-contrast pointer-events-none" />
<div class="flex items-center gap-1 ml-3">
<button
class="cursor-pointer p-0 m-0 border-none outline-none bg-button-bg rounded-full flex items-center justify-center w-6 h-6 hover:brightness-75 transition-all"
@click="router.back()"
>
<LeftArrowIcon />
</button>
<button
class="cursor-pointer p-0 m-0 border-none outline-none bg-button-bg rounded-full flex items-center justify-center w-6 h-6 hover:brightness-75 transition-all"
@click="router.forward()"
>
<RightArrowIcon />
</button>
</div>
<Breadcrumbs class="pt-[2px]" /> <Breadcrumbs class="pt-[2px]" />
</div> </div>
<section class="flex ml-auto items-center"> <section class="flex ml-auto items-center">
@@ -704,7 +713,7 @@ function handleAuxClick(e) {
display: grid; display: grid;
grid-template-columns: 1fr 0px; grid-template-columns: 1fr 0px;
transition: grid-template-columns 0.4s ease-in-out; // transition: grid-template-columns 0.4s ease-in-out;
&.sidebar-enabled { &.sidebar-enabled {
grid-template-columns: 1fr 300px; grid-template-columns: 1fr 300px;

View File

@@ -35,10 +35,12 @@ const props = defineProps({
}) })
const playing = ref(false) const playing = ref(false)
const loading = ref(false)
const modLoading = computed( const modLoading = computed(
() => () =>
currentEvent.value === 'installing' || (currentEvent.value === 'launched' && !playing.value), loading.value ||
currentEvent.value === 'installing' ||
(currentEvent.value === 'launched' && !playing.value),
) )
const installing = computed(() => props.instance.install_stage !== 'installed') const installing = computed(() => props.instance.install_stage !== 'installed')
@@ -56,6 +58,7 @@ const checkProcess = async () => {
const play = async (e, context) => { const play = async (e, context) => {
e?.stopPropagation() e?.stopPropagation()
loading.value = true
await run(props.instance.path) await run(props.instance.path)
.catch((err) => handleSevereError(err, { profilePath: props.instance.path })) .catch((err) => handleSevereError(err, { profilePath: props.instance.path }))
.finally(() => { .finally(() => {
@@ -65,6 +68,7 @@ const play = async (e, context) => {
source: context, source: context,
}) })
}) })
loading.value = false
} }
const stop = async (e, context) => { const stop = async (e, context) => {
@@ -118,7 +122,7 @@ onUnmounted(() => unlisten())
<template> <template>
<template v-if="compact"> <template v-if="compact">
<div <div
class="button-base card-shadow grid grid-cols-[auto_1fr_auto] bg-bg-raised rounded-xl p-3 pl-4 gap-2 cursor-pointer active:scale-[0.98] transition-transform" class="card-shadow grid grid-cols-[auto_1fr_auto] bg-bg-raised rounded-xl p-3 pl-4 gap-2 cursor-pointer hover:brightness-90 transition-all"
@click="seeInstance" @click="seeInstance"
@mouseenter="checkProcess" @mouseenter="checkProcess"
> >

View File

@@ -60,7 +60,7 @@ const toTransparent = computed(() => {
<template> <template>
<div <div
class="card-shadow button-base bg-bg-raised rounded-xl overflow-clip cursor-pointer active:scale-[0.98] transition-transform" class="card-shadow bg-bg-raised rounded-xl overflow-clip cursor-pointer hover:brightness-90 transition-all"
@click="router.push(`/project/${project.slug}`)" @click="router.push(`/project/${project.slug}`)"
> >
<div <div

View File

@@ -1,7 +1,6 @@
<script setup> <script setup>
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { ChevronRightIcon } from '@modrinth/assets' import { init_ads_window } from '@/helpers/ads.js'
import { init_ads_window, open_ads_link, record_ads_click } from '@/helpers/ads.js'
const adsWrapper = ref(null) const adsWrapper = ref(null)
@@ -29,27 +28,12 @@ function updateAdPosition() {
initDevicePixelRatioWatcher() initDevicePixelRatioWatcher()
} }
} }
async function openPlusLink() {
await record_ads_click()
await open_ads_link('https://modrinth.com/plus', 'https://modrinth.com')
}
</script> </script>
<template> <template>
<div ref="adsWrapper" class="ad-parent relative flex w-full justify-center cursor-pointer bg-bg"> <div ref="adsWrapper" class="ad-parent relative flex w-full justify-center cursor-pointer bg-bg">
<div class="flex max-h-[250px] min-h-[250px] min-w-[300px] max-w-[300px] flex-col gap-4 p-6"> <div class="flex max-h-[250px] min-h-[250px] min-w-[300px] max-w-[300px] flex-col gap-4 p-6">
<p class="m-0 text-2xl font-bold text-contrast">75% of ad revenue goes to creators</p> <p class="m-0 text-2xl font-bold text-contrast">75% of ad revenue goes to creators</p>
<button
class="mt-auto items-center gap-1 text-purple hover:underline bg-transparent border-none text-left cursor-pointer outline-none"
@click="openPlusLink"
>
<span>
Support creators and Modrinth ad-free with
<span class="font-bold">Modrinth+</span>
</span>
<ChevronRightIcon class="relative top-[3px] h-5 w-5" />
</button>
</div> </div>
</div> </div>
</template> </template>

View File

@@ -1,6 +1,6 @@
<template> <template>
<div <div
class="card-shadow button-base p-4 bg-bg-raised rounded-xl flex gap-3 group" class="card-shadow p-4 bg-bg-raised rounded-xl flex gap-3 group cursor-pointer hover:brightness-90 transition-all"
@click=" @click="
() => { () => {
emit('open') emit('open')
@@ -12,21 +12,7 @@
" "
> >
<div class="icon w-[96px] h-[96px] relative"> <div class="icon w-[96px] h-[96px] relative">
<Avatar <Avatar :src="project.icon_url" size="96px" class="search-icon origin-top transition-all" />
:src="project.icon_url"
size="96px"
class="search-icon origin-top transition-all"
:class="{ 'scale-[0.85]': installed, 'brightness-50': installing }"
/>
<div v-if="installing" class="rounded-2xl absolute inset-0 flex items-center justify-center">
<SpinnerIcon class="h-8 w-8 animate-spin" />
</div>
<div
v-if="installed"
class="absolute shadow-sm font-semibold bottom-0 w-full p-1 bg-button-bg rounded-full text-xs justify-center items-center flex gap-1 text-brand border-[1px] border-solid border-[--color-button-border]"
>
<CheckIcon class="shrink-0 stroke-[3px]" /> Installed
</div>
</div> </div>
<div class="flex flex-col gap-2 overflow-hidden"> <div class="flex flex-col gap-2 overflow-hidden">
<div class="gap-2 overflow-hidden no-wrap text-ellipsis"> <div class="gap-2 overflow-hidden no-wrap text-ellipsis">
@@ -40,6 +26,42 @@
</div> </div>
<div v-if="categories.length > 0" class="mt-auto flex items-center gap-1 no-wrap"> <div v-if="categories.length > 0" class="mt-auto flex items-center gap-1 no-wrap">
<TagsIcon class="h-4 w-4 shrink-0" /> <TagsIcon class="h-4 w-4 shrink-0" />
<div
v-if="project.project_type === 'mod' || project.project_type === 'modpack'"
class="text-sm font-semibold text-secondary flex gap-1 px-[0.375rem] py-0.5 bg-button-bg rounded-full"
>
<template v-if="project.client_side === 'optional' && project.server_side === 'optional'">
Client or server
</template>
<template
v-else-if="
(project.client_side === 'optional' || project.client_side === 'required') &&
(project.server_side === 'optional' || project.server_side === 'unsupported')
"
>
Client
</template>
<template
v-else-if="
(project.server_side === 'optional' || project.server_side === 'required') &&
(project.client_side === 'optional' || project.client_side === 'unsupported')
"
>
Server
</template>
<template
v-else-if="
project.client_side === 'unsupported' && project.server_side === 'unsupported'
"
>
Unsupported
</template>
<template
v-else-if="project.client_side === 'required' && project.server_side === 'required'"
>
Client and server
</template>
</div>
<div <div
v-for="tag in categories" v-for="tag in categories"
:key="tag" :key="tag"
@@ -65,19 +87,8 @@
</span> </span>
</div> </div>
<div class="mt-auto relative"> <div class="mt-auto relative">
<div <div class="absolute bottom-0 right-0 w-fit">
class="flex items-center gap-2 group-hover:-translate-y-3 group-hover:opacity-0 group-focus-within:opacity-0 group-hover:scale-95 group-focus-within:scale-95 transition-all" <ButtonStyled color="brand" type="outlined">
>
<HistoryIcon class="shrink-0" />
<span>
<span class="text-secondary">Updated</span>
{{ dayjs(project.date_modified ?? project.updated).fromNow() }}
</span>
</div>
<div
class="opacity-0 scale-95 translate-y-3 group-hover:translate-y-0 group-hover:scale-100 group-hover:opacity-100 group-focus-within:opacity-100 group-focus-within:scale-100 absolute bottom-0 right-0 transition-all w-fit"
>
<ButtonStyled color="brand">
<button <button
:disabled="installed || installing" :disabled="installed || installing"
class="shrink-0 no-wrap" class="shrink-0 no-wrap"
@@ -106,15 +117,7 @@
</template> </template>
<script setup> <script setup>
import { import { TagsIcon, DownloadIcon, HeartIcon, PlusIcon, CheckIcon } from '@modrinth/assets'
SpinnerIcon,
TagsIcon,
DownloadIcon,
HeartIcon,
PlusIcon,
CheckIcon,
HistoryIcon,
} from '@modrinth/assets'
import { ButtonStyled, Avatar } from '@modrinth/ui' import { ButtonStyled, Avatar } from '@modrinth/ui'
import { formatNumber, formatCategory } from '@modrinth/utils' import { formatNumber, formatCategory } from '@modrinth/utils'
import dayjs from 'dayjs' import dayjs from 'dayjs'

View File

@@ -356,12 +356,6 @@ const messages = defineMessages({
const options = ref(null) const options = ref(null)
const handleRightClick = (event, result) => { const handleRightClick = (event, result) => {
options.value.showMenu(event, result, [ options.value.showMenu(event, result, [
{
name: 'install',
},
{
type: 'divider',
},
{ {
name: 'open_link', name: 'open_link',
}, },

View File

@@ -176,15 +176,18 @@
</button> </button>
</ButtonStyled> </ButtonStyled>
<div v-else class="w-[36px]"></div> <div v-else class="w-[36px]"></div>
<Toggle
class="!mx-2"
:model-value="!item.data.disabled"
:checked="!item.data.disabled"
@update:model-value="toggleDisableMod(item.data)"
/>
<ButtonStyled type="transparent" circular> <ButtonStyled type="transparent" circular>
<button <button v-tooltip="'Remove'" @click="removeMod(item)">
v-tooltip="item.disabled ? `Enable` : `Disable`" <TrashIcon />
@click="toggleDisableMod(item.data)"
>
<CheckCircleIcon v-if="item.disabled" />
<SlashIcon v-else />
</button> </button>
</ButtonStyled> </ButtonStyled>
<ButtonStyled type="transparent" circular> <ButtonStyled type="transparent" circular>
<OverflowMenu <OverflowMenu
:options="[ :options="[
@@ -197,23 +200,12 @@
shown: item.data !== undefined && item.data.slug !== undefined, shown: item.data !== undefined && item.data.slug !== undefined,
action: () => copyModLink(item), action: () => copyModLink(item),
}, },
{
divider: true,
},
{
id: 'remove',
color: 'red',
action: () => removeMod(item),
},
]" ]"
direction="left" direction="left"
> >
<MoreVerticalIcon /> <MoreVerticalIcon />
<template #show-file> <ExternalIcon /> Show file </template> <template #show-file> <ExternalIcon /> Show file </template>
<template #copy-link> <ClipboardCopyIcon /> Copy link </template> <template #copy-link> <ClipboardCopyIcon /> Copy link </template>
<template v-if="item.disabled" #toggle> <CheckCircleIcon /> Enable </template>
<template v-else #toggle> <SlashIcon /> Disable </template>
<template #remove> <TrashIcon /> Remove </template>
</OverflowMenu> </OverflowMenu>
</ButtonStyled> </ButtonStyled>
</template> </template>
@@ -275,7 +267,14 @@ import {
UpdatedIcon, UpdatedIcon,
XIcon, XIcon,
} from '@modrinth/assets' } from '@modrinth/assets'
import { Button, ButtonStyled, ContentListPanel, OverflowMenu, Pagination } from '@modrinth/ui' import {
Button,
ButtonStyled,
ContentListPanel,
OverflowMenu,
Pagination,
Toggle,
} from '@modrinth/ui'
import { formatProjectType } from '@modrinth/utils' import { formatProjectType } from '@modrinth/utils'
import type { ComputedRef } from 'vue' import type { ComputedRef } from 'vue'
import { computed, onUnmounted, ref, watch } from 'vue' import { computed, onUnmounted, ref, watch } from 'vue'
@@ -462,6 +461,10 @@ const messages = defineMessages({
id: 'instance.filter.updates-available', id: 'instance.filter.updates-available',
defaultMessage: 'Updates available', defaultMessage: 'Updates available',
}, },
disabledFilter: {
id: 'instance.filter.disabled',
defaultMessage: 'Disabled projects',
},
}) })
const filterOptions: ComputedRef<FilterOption[]> = computed(() => { const filterOptions: ComputedRef<FilterOption[]> = computed(() => {
@@ -488,19 +491,30 @@ const filterOptions: ComputedRef<FilterOption[]> = computed(() => {
}) })
} }
if (projects.value.some((m) => m.disabled)) {
options.push({
id: 'disabled',
formattedName: formatMessage(messages.disabledFilter),
})
}
return options return options
}) })
const selectedFilters = ref([]) const selectedFilters = ref([])
const filteredProjects = computed(() => { const filteredProjects = computed(() => {
const updatesFilter = selectedFilters.value.includes('updates') const updatesFilter = selectedFilters.value.includes('updates')
const disabledFilter = selectedFilters.value.includes('disabled')
const typeFilters = selectedFilters.value.filter((filter) => filter !== 'updates') const typeFilters = selectedFilters.value.filter(
(filter) => filter !== 'updates' && filter !== 'disabled',
)
return projects.value.filter((project) => { return projects.value.filter((project) => {
return ( return (
(typeFilters.length === 0 || typeFilters.includes(project.project_type)) && (typeFilters.length === 0 || typeFilters.includes(project.project_type)) &&
(!updatesFilter || project.outdated) (!updatesFilter || project.outdated) &&
(!disabledFilter || project.disabled)
) )
}) })
}) })

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "theseus_gui" name = "theseus_gui"
version = "0.9.0" version = "0.9.1"
description = "The Modrinth App is a desktop application for managing your Minecraft mods" description = "The Modrinth App is a desktop application for managing your Minecraft mods"
license = "GPL-3.0-only" license = "GPL-3.0-only"
repository = "https://github.com/modrinth/code/apps/app/" repository = "https://github.com/modrinth/code/apps/app/"

View File

@@ -21,3 +21,86 @@ document.addEventListener(
window.open = (url, target, features) => { window.open = (url, target, features) => {
window.top.postMessage({ modrinthOpenUrl: url }, 'https://modrinth.com') window.top.postMessage({ modrinthOpenUrl: url }, 'https://modrinth.com')
} }
function muteAudioContext() {
if (window.AudioContext || window.webkitAudioContext) {
const AudioContext = window.AudioContext || window.webkitAudioContext
const originalCreateMediaElementSource = AudioContext.prototype.createMediaElementSource
const originalCreateMediaStreamSource = AudioContext.prototype.createMediaStreamSource
const originalCreateMediaStreamTrackSource = AudioContext.prototype.createMediaStreamTrackSource
const originalCreateBufferSource = AudioContext.prototype.createBufferSource
const originalCreateOscillator = AudioContext.prototype.createOscillator
AudioContext.prototype.createGain = function () {
const gain = originalCreateGain.call(this)
gain.gain.value = 0
return gain
}
AudioContext.prototype.createMediaElementSource = function (mediaElement) {
const source = originalCreateMediaElementSource.call(this, mediaElement)
source.connect(this.createGain())
return source
}
AudioContext.prototype.createMediaStreamSource = function (mediaStream) {
const source = originalCreateMediaStreamSource.call(this, mediaStream)
source.connect(this.createGain())
return source
}
AudioContext.prototype.createMediaStreamTrackSource = function (mediaStreamTrack) {
const source = originalCreateMediaStreamTrackSource.call(this, mediaStreamTrack)
source.connect(this.createGain())
return source
}
AudioContext.prototype.createBufferSource = function () {
const source = originalCreateBufferSource.call(this)
source.connect(this.createGain())
return source
}
AudioContext.prototype.createOscillator = function () {
const oscillator = originalCreateOscillator.call(this)
oscillator.connect(this.createGain())
return oscillator
}
}
}
function muteVideo(mediaElement) {
let count = Number(mediaElement.getAttribute('data-modrinth-muted-count') ?? 0)
if (!mediaElement.muted || mediaElement.volume !== 0) {
mediaElement.muted = true
mediaElement.volume = 0
mediaElement.setAttribute('data-modrinth-muted-count', count + 1)
}
if (count > 5) {
// Video is detected as malicious, so it is removed from the page
mediaElement.remove()
}
}
function muteVideos() {
document.querySelectorAll('video, audio').forEach(function (mediaElement) {
muteVideo(mediaElement)
if (!mediaElement.hasAttribute('data-modrinth-muted')) {
mediaElement.addEventListener('volumechange', () => muteVideo(mediaElement))
mediaElement.setAttribute('data-modrinth-muted', 'true')
}
})
}
document.addEventListener('DOMContentLoaded', () => {
muteVideos()
muteAudioContext()
const observer = new MutationObserver(muteVideos)
observer.observe(document.body, { childList: true, subtree: true })
})

View File

@@ -44,7 +44,7 @@
] ]
}, },
"productName": "Modrinth App", "productName": "Modrinth App",
"version": "0.9.0", "version": "0.9.1",
"mainBinaryName": "Modrinth App", "mainBinaryName": "Modrinth App",
"identifier": "ModrinthApp", "identifier": "ModrinthApp",
"plugins": { "plugins": {

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "theseus" name = "theseus"
version = "0.9.0" version = "0.9.1"
authors = ["Jai A <jaiagr+gpg@pm.me>"] authors = ["Jai A <jaiagr+gpg@pm.me>"]
edition = "2021" edition = "2021"

View File

@@ -83,7 +83,7 @@ const model = defineModel<boolean>()
</div> </div>
<div class="text-secondary text-xs line-clamp-1 break-all">{{ item.filename }}</div> <div class="text-secondary text-xs line-clamp-1 break-all">{{ item.filename }}</div>
</div> </div>
<div class="flex justify-end gap-1"> <div class="flex justify-end gap-1 items-center">
<slot name="actions" :item="item" /> <slot name="actions" :item="item" />
</div> </div>
</div> </div>

View File

@@ -27,7 +27,7 @@
</section> </section>
<section <section
v-if=" v-if="
(project.actualProjectType === 'mod' || project.project_type === 'modpack') && (project.project_type === 'mod' || project.project_type === 'modpack') &&
!(project.client_side === 'unsupported' && project.server_side === 'unsupported') && !(project.client_side === 'unsupported' && project.server_side === 'unsupported') &&
!(project.client_side === 'unknown' && project.server_side === 'unknown') !(project.client_side === 'unknown' && project.server_side === 'unknown')
" "