Initial commit

This commit is contained in:
2024-09-01 06:20:49 +03:00
parent bd61f5d591
commit 9263c396a1
81 changed files with 1494 additions and 1521 deletions

View File

@@ -1,7 +1,8 @@
{
"name": "@modrinth/app-frontend",
"private": true,
"version": "0.8.5",
"version": "0.8.501",
"development_build": true,
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -8,7 +8,7 @@ import {
PlusIcon,
SettingsIcon,
XIcon,
DownloadIcon,
// DownloadIcon,
} from '@modrinth/assets'
import { Button, Notifications } from '@modrinth/ui'
import { useLoading, useTheming } from '@/store/state'
@@ -24,7 +24,7 @@ import { handleError, useNotifications } from '@/store/notifications.js'
import { command_listener, warning_listener } from '@/helpers/events.js'
import { MinimizeIcon, MaximizeIcon } from '@/assets/icons'
import { type } from '@tauri-apps/plugin-os'
import { isDev, getOS, restartApp } from '@/helpers/utils.js'
import { isDev, getOS } from '@/helpers/utils.js'
import { initAnalytics, debugAnalytics, optOutAnalytics, trackEvent } from '@/helpers/analytics'
import { getCurrentWindow } from '@tauri-apps/api/window'
import { getVersion } from '@tauri-apps/api/app'
@@ -42,7 +42,7 @@ import { get_opening_command, initialize_state } from '@/helpers/state'
import { saveWindowState, StateFlags } from '@tauri-apps/plugin-window-state'
import { renderString } from '@modrinth/utils'
import { useFetch } from '@/helpers/fetch.js'
import { check } from '@tauri-apps/plugin-updater'
// import { check } from '@tauri-apps/plugin-updater'
const themeStore = useTheming()
@@ -99,6 +99,7 @@ async function setupApp() {
initAnalytics()
if (!telemetry) {
console.info("[AstralRinth] Telemetry disabled by default (Hard patched in code).")
optOutAnalytics()
}
if (dev) debugAnalytics()
@@ -121,18 +122,18 @@ async function setupApp() {
}),
)
useFetch(
`https://api.modrinth.com/appCriticalAnnouncement.json?version=${version}`,
'criticalAnnouncements',
true,
).then((res) => {
if (res && res.header && res.body) {
criticalErrorMessage.value = res
}
})
// useFetch(
// `https://api.modrinth.com/appCriticalAnnouncement.json?version=${version}`,
// 'criticalAnnouncements',
// true,
// ).then((res) => {
// if (res && res.header && res.body) {
// criticalErrorMessage.value = res
// }
// })
get_opening_command().then(handleCommand)
checkUpdates()
// checkUpdates()
}
const stateFailed = ref(false)
@@ -243,19 +244,19 @@ async function handleCommand(e) {
}
}
const updateAvailable = ref(false)
async function checkUpdates() {
const update = await check()
console.log(update)
updateAvailable.value = !!update
// const updateAvailable = ref(false)
// async function checkUpdates() {
// const update = await check()
// console.log(update)
// updateAvailable.value = !!update
setTimeout(
() => {
checkUpdates()
},
5 * 1000 * 60,
)
}
// setTimeout(
// () => {
// checkUpdates()
// },
// 5 * 1000 * 60,
// )
// }
</script>
<template>
@@ -289,14 +290,14 @@ async function checkUpdates() {
</div>
</div>
<div class="settings pages-list">
<button
<!-- <button
v-if="updateAvailable"
v-tooltip="'Install update'"
class="btn btn-outline btn-primary icon-only collapsed-button"
@click="restartApp()"
>
<DownloadIcon />
</button>
</button> -->
<Button
v-tooltip="'Create profile'"
class="sleek-primary collapsed-button"

View File

@@ -12,3 +12,6 @@ export { default as NewInstanceImage } from './new-instance.svg'
export { default as MenuIcon } from './menu.svg'
export { default as BugIcon } from './bug.svg'
export { default as ChatIcon } from './messages-square.svg'
export { default as Pirate } from './pirate.svg'
export { default as Microsoft } from './microsoft.svg'
export { default as PirateShip } from './pirate-ship.svg'

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="10mm" height="10mm" viewBox="0 0 10 10" version="1.1" id="svg26662" inkscape:version="1.2.2 (732a01da63, 2022-12-09)" sodipodi:docname="lic.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview id="namedview26664" pagecolor="#505050" bordercolor="#eeeeee" borderopacity="1" inkscape:showpageshadow="0" inkscape:pageopacity="0" inkscape:pagecheckerboard="0" inkscape:deskcolor="#505050" inkscape:document-units="mm" showgrid="false" inkscape:zoom="14.638496" inkscape:cx="17.112414" inkscape:cy="34.258984" inkscape:window-width="1488" inkscape:window-height="1230" inkscape:window-x="2794" inkscape:window-y="123" inkscape:window-maximized="0" inkscape:current-layer="layer1" />
<defs id="defs26659" />
<g inkscape:label="Слой 1" inkscape:groupmode="layer" id="layer1">
<g id="g26657" transform="matrix(3.9940568,0,0,3.954914,-1082.6556,16143.384)">
<path id="path26649" style="fill:#05a6f0;fill-opacity:1;stroke-width:0.18168;stroke-miterlimit:5.8;paint-order:fill markers stroke" d="m 271.06666,-4080.4646 v 0.8986 c 0,0.1289 0.0997,0.2334 0.22634,0.2398 h 0.91261 v -1.1384 z" />
<path id="path26651" style="fill:#ffba08;fill-opacity:1;stroke-width:0.18168;stroke-miterlimit:5.8;paint-order:fill markers stroke" d="m 272.43195,-4080.4646 v 1.1384 h 0.89917 0.0124 c 0.12669,-0.01 0.22686,-0.1109 0.22686,-0.2398 v -0.8986 z" />
<path id="path26653" style="fill:#e25127;fill-opacity:1;stroke-width:0.18168;stroke-miterlimit:5.8;paint-order:fill markers stroke" d="m 271.3054,-4081.8547 c -0.13246,0 -0.23874,0.1073 -0.23874,0.2403 v 0.8955 h 1.13895 v -1.1358 z" />
<path id="path26655" style="fill:#81bc06;fill-opacity:1;stroke:none;stroke-width:0.184849;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:5.8;stroke-dasharray:none;stroke-opacity:1;paint-order:normal" d="m 272.43195,-4081.8547 v 1.1358 h 1.13843 v -0.8955 c 0,-0.133 -0.1068,-0.2403 -0.23926,-0.2403 z" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<svg height="800px" width="800px" version="1.1" id="Layer_1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 511.672 511.672" xml:space="preserve">
<path style="fill:#ED5564;" d="M227.674,44.901c0,0,0.047-0.031,0.141-0.109c-0.031,0.031-3.342,2.437-9.088,3.514
c-7.745,1.437-16.208-0.109-25.14-4.591c-31.386-15.771-68.175-0.968-69.721-0.344l0.016,0.062c-3.88,1.593-6.621,5.403-6.621,9.869
c0,5.887,4.771,10.649,10.657,10.649c1.694,0,3.295-0.406,4.716-1.109c4.466-1.624,30.816-10.416,51.381-0.078
c10.954,5.497,20.706,7.371,28.919,7.371c17.028,0,27.42-8.073,28.044-8.573L227.674,44.901z"/>
<g>
<path style="fill:#7F4545;" d="M234.514,31.973c-5.887,0-10.657,4.778-10.657,10.665v351.704h21.322V42.638
C245.179,36.751,240.401,31.973,234.514,31.973z"/>
<path style="fill:#7F4545;" d="M511.672,319.655c0-5.887-4.777-10.665-10.648-10.665c-1.031,0-2.016,0.156-2.951,0.422l0,0
l-0.234,0.062l0,0l-108.213,31.073l5.871,20.487l108.463-31.137l0,0C508.408,328.618,511.672,324.512,511.672,319.655z"/>
</g>
<path style="fill:#A85D5D;" d="M10.689,308.99l99.725,33.385c0,0,39.116,41.239,124.1,41.239c85,0,106.611-29.138,106.611-29.138
l85.258-24.484c9.588,160.21-122.656,149.561-122.656,149.561H115.294c-86.171-5.996-73.633-88.568-73.633-88.568l-13.187-9.728
l-4.208-18.021L0,351.635L10.689,308.99z"/>
<path style="fill:#965353;" d="M426.664,335.317c-5.871,132.337-122.938,122.905-122.938,122.905H115.294
c-57.409-3.981-71.001-41.973-73.68-66.879c-0.765,5.84-9.205,82.432,73.68,88.209h188.433
C303.727,479.553,433.004,489.968,426.664,335.317z"/>
<g>
<path style="fill:#434A54;" d="M277.166,415.594c0,5.887,4.763,10.649,10.649,10.649c5.888,0,10.649-4.763,10.649-10.649
s-4.762-10.665-10.649-10.665C281.929,404.929,277.166,409.707,277.166,415.594z"/>
<path style="fill:#434A54;" d="M234.514,415.594c0,5.887,4.778,10.649,10.665,10.649s10.657-4.763,10.657-10.649
s-4.77-10.665-10.657-10.665S234.514,409.707,234.514,415.594z"/>
<path style="fill:#434A54;" d="M191.877,415.594c0,5.887,4.771,10.649,10.657,10.649s10.665-4.763,10.665-10.649
s-4.778-10.665-10.665-10.665S191.877,409.707,191.877,415.594z"/>
<path style="fill:#434A54;" d="M149.24,415.594c0,5.887,4.771,10.649,10.657,10.649s10.657-4.763,10.657-10.649
s-4.771-10.665-10.657-10.665S149.24,409.707,149.24,415.594z"/>
<path style="fill:#434A54;" d="M99.569,330.305C114.942,207.719,74.616,95.869,74.616,95.869h260.169
c80.105,93.783,24.953,234.561,24.953,234.561C262.832,285.849,99.569,330.305,99.569,330.305z"/>
</g>
<g style="opacity:0.1;">
<path style="fill:#FFFFFF;" d="M334.785,95.869h-21.314c69.206,81.026,37.445,197.147,27.529,227.222
c6.434,2.123,12.695,4.56,18.738,7.339C359.738,330.43,414.891,189.652,334.785,95.869z"/>
</g>
<path style="fill:#E6E9ED;" d="M170.555,196.477c0-35.321,28.638-63.959,63.959-63.959c35.329,0,63.951,28.638,63.951,63.959
c0,18.941-8.213,35.961-21.299,47.672v26.952h-85.289v-26.952C178.792,232.438,170.555,215.418,170.555,196.477z"/>
<g>
<path style="fill:#434A54;" d="M250.503,196.477c0,5.887,4.778,10.665,10.665,10.665c5.888,0,10.658-4.778,10.658-10.665
s-4.771-10.665-10.658-10.665C255.282,185.812,250.503,190.59,250.503,196.477z"/>
<path style="fill:#434A54;" d="M197.21,196.477c0,5.887,4.771,10.665,10.657,10.665s10.657-4.778,10.657-10.665
s-4.771-10.665-10.657-10.665S197.21,190.59,197.21,196.477z"/>
</g>
<g>
<path style="fill:#CCD1D9;" d="M277.166,271.085c-0.016-5.871-4.777-10.649-10.673-10.649c-5.887,0-10.657,4.778-10.657,10.665
h21.33V271.085z"/>
<path style="fill:#CCD1D9;" d="M255.836,271.085c0-5.871-4.77-10.649-10.657-10.649s-10.665,4.778-10.665,10.665h21.322V271.085z" />
<path style="fill:#CCD1D9;" d="M234.514,271.085c0-5.871-4.771-10.649-10.657-10.649s-10.657,4.778-10.657,10.665h21.314V271.085z" />
<path style="fill:#CCD1D9;" d="M213.199,271.085c-0.008-5.871-4.778-10.649-10.665-10.649s-10.657,4.778-10.657,10.665h21.322
L213.199,271.085L213.199,271.085z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="10mm" height="10mm" viewBox="0 0 10 10" version="1.1" id="svg26662" inkscape:version="1.2.2 (732a01da63, 2022-12-09)" sodipodi:docname="pir.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview id="namedview26664" pagecolor="#505050" bordercolor="#eeeeee" borderopacity="1" inkscape:showpageshadow="0" inkscape:pageopacity="0" inkscape:pagecheckerboard="0" inkscape:deskcolor="#505050" inkscape:document-units="mm" showgrid="false" inkscape:zoom="10.35098" inkscape:cx="16.375261" inkscape:cy="42.073312" inkscape:window-width="1488" inkscape:window-height="1230" inkscape:window-x="2794" inkscape:window-y="123" inkscape:window-maximized="0" inkscape:current-layer="layer1" />
<defs id="defs26659" />
<g inkscape:label="Слой 1" inkscape:groupmode="layer" id="layer1">
<path id="path26647" style="fill:#e7f9fb;fill-opacity:1;stroke:none;stroke-width:0.734686;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:5.8;stroke-dasharray:none;stroke-opacity:1;paint-order:normal" d="M 0.953646,0 C 0.4245958,0 0,0.42377 0,0.950056 V 9.051658 C 0,9.577943 0.4245958,9.9999995 0.953646,9.9999995 H 9.044545 C 9.573595,9.9999995 10,9.577943 10,9.051658 V 0.950056 C 10,0.42377 9.573595,0 9.044545,0 Z m 4.0319653,0.680202 c 0.7122257,0 1.1971907,0.171336 1.6235677,0.587681 C 7.149602,1.795597 7.303455,2.529484 7.076165,3.503527 6.954393,4.024672 6.743646,4.411034 6.384675,4.767983 6.046182,5.105514 5.7597,5.274565 5.369942,5.364516 4.7470347,5.510152 4.177518,5.313116 3.618915,4.763985 3.260174,4.411034 3.0504322,4.028385 2.9274247,3.503527 2.6985267,2.5272 2.8504547,1.797596 3.392573,1.267883 3.8051351,0.866387 4.2970505,0.680202 4.9856113,0.680202 Z M 4.1253339,2.785916 C 3.7727965,2.786202 3.5578276,2.980097 3.5578276,3.29821 c 0,0.197035 0.074385,0.320683 0.2711164,0.455467 C 4.0618628,3.91359 4.4444125,3.833637 4.5886441,3.596619 4.6788251,3.450984 4.6839941,3.154002 4.6001321,3.01265 4.5082281,2.855593 4.3583101,2.784203 4.1277751,2.784203 Z m 1.7295441,0 c -0.20506,0 -0.404923,0.09423 -0.493868,0.26557 C 5.226026,3.311345 5.34091,3.641452 5.60714,3.779948 5.859588,3.90845 6.185962,3.822778 6.372268,3.574345 6.463308,3.45441 6.469338,3.164568 6.383758,3.033211 6.273759,2.864731 6.062581,2.784774 5.85752,2.784774 Z M 1.8659927,5.307119 c 0.00862,-5.71e-4 0.020104,0 0.03073,0 0.2179844,2.86e-4 0.5876389,0.16848 1.6846262,0.695051 C 4.3488321,6.369399 4.9972712,6.669808 5.018122,6.669808 5.038222,6.669237 5.68049,6.371113 6.440564,6.008738 7.200522,5.64465 7.88377,5.323396 7.958155,5.323396 8.14742,5.283416 8.403142,5.409066 8.509492,5.577544 8.663431,5.82598 8.604552,6.167795 8.374795,6.361404 8.316205,6.409954 6.898963,7.091579 5.226428,7.875439 2.6791977,9.069933 2.1554033,9.312944 1.9972712,9.312944 c -0.2153996,0 -0.2679571,0 -0.3915391,-0.114223 C 1.3150575,8.936006 1.3506989,8.509952 1.6827021,8.264656 1.7519171,8.213256 2.1996325,7.990519 2.6777048,7.764356 3.1557197,7.54162 3.5582872,7.334304 3.5756915,7.334304 c 0.01436,0 -0.3826933,-0.217025 -0.8872167,-0.448042 C 2.1839513,6.654959 1.7142365,6.421657 1.646802,6.369971 1.4819495,6.24718 1.4026824,6.080128 1.4026824,5.863103 c 0,-0.219881 0.09535,-0.394643 0.2731268,-0.491448 0.063758,-0.03141 0.1203366,-0.05711 0.1901261,-0.06282 z M 7.094115,7.62329 7.661622,7.905994 c 0.784456,0.383506 0.930354,0.522573 0.930354,0.881807 0,0.219881 -0.105403,0.397498 -0.280307,0.480311 -0.151641,0.06568 -0.331859,0.06853 -0.513628,0 C 7.59815,9.193862 5.819639,8.388875 5.788334,8.354036 c -0.01436,0 0.273414,-0.182758 0.639363,-0.378651 z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -1,68 +1,89 @@
<template>
<div
v-if="mode !== 'isolated'"
ref="button"
v-tooltip.right="'Minecraft accounts'"
class="button-base avatar-button"
:class="{ expanded: mode === 'expanded' }"
@click="toggleMenu"
>
<Avatar
:size="mode === 'expanded' ? 'xs' : 'sm'"
:src="
selectedAccount
? `https://mc-heads.net/avatar/${selectedAccount.id}/128`
: 'https://launcher-files.modrinth.com/assets/steve_head.png'
"
/>
<div v-if="mode !== 'isolated'" ref="button" v-tooltip.right="'Minecraft accounts'" class="button-base avatar-button"
:class="{ expanded: mode === 'expanded' }" @click="toggleMenu">
<Avatar :size="mode === 'expanded' ? 'xs' : 'sm'" :src="selectedAccount
? `https://mc-heads.net/avatar/${selectedAccount.username}/128`
: 'https://launcher-files.modrinth.com/assets/steve_head.png'
" />
</div>
<transition name="fade">
<Card
v-if="showCard || mode === 'isolated'"
ref="card"
class="account-card"
:class="{ expanded: mode === 'expanded', isolated: mode === 'isolated' }"
>
<Card v-if="showCard || mode === 'isolated'" ref="card" class="account-card"
:class="{ expanded: mode === 'expanded', isolated: mode === 'isolated' }">
<div v-if="selectedAccount" class="selected account">
<Avatar size="xs" :src="`https://mc-heads.net/avatar/${selectedAccount.id}/128`" />
<Avatar size="xs" :src="`https://mc-heads.net/avatar/${selectedAccount.username}/128`" />
<div>
<h4>{{ selectedAccount.username }}</h4>
<h4>
<component :is="getAccountType(selectedAccount)" class="vector-icon" /> {{ selectedAccount.username }}
</h4>
<p>Selected</p>
</div>
<Button v-tooltip="'Log out'" icon-only color="raised" @click="logout(selectedAccount.id)">
<TrashIcon />
</Button>
</div>
<div v-else class="logged-out account">
<div v-else class="login-section account">
<h4>Not signed in</h4>
<Button v-tooltip="'Log in'" icon-only color="primary" @click="login()">
<LogInIcon />
<Button v-tooltip="'Log in'" icon-only @click="login()">
<MicrosoftIcon />
</Button>
<Button v-tooltip="'Add offline'" icon-only @click="tryOfflineLogin()">
<PirateIcon />
</Button>
</div>
<div v-if="displayAccounts.length > 0" class="account-group">
<div v-for="account in displayAccounts" :key="account.id" class="account-row">
<Button class="option account" @click="setAccount(account)">
<Avatar :src="`https://mc-heads.net/avatar/${account.id}/128`" class="icon" />
<p>{{ account.username }}</p>
<Avatar :src="`https://mc-heads.net/avatar/${account.username}/128`" class="icon" />
<p class="account-type">
<component :is="getAccountType(account)" class="vector-icon" />
{{ account.username }}
</p>
</Button>
<Button v-tooltip="'Log out'" icon-only @click="logout(account.id)">
<TrashIcon />
</Button>
</div>
</div>
<Button v-if="accounts.length > 0" @click="login()">
<PlusIcon />
Add account
</Button>
<div v-if="accounts.length > 0" class="login-section account centered">
<Button v-tooltip="'Log in'" icon-only @click="login()">
<MicrosoftIcon />
</Button>
<Button v-tooltip="'Add offline'" icon-only @click="tryOfflineLogin()">
<PirateIcon />
</Button>
</div>
</Card>
</transition>
<ModalWrapper ref="loginOfflineModal" class="modal" header="Offline auth">
<div class="modal-body">
<div class="label">Offline account</div>
<input type="text" v-model="playerName" placeholder="Provide offline player name" />
<Button icon-only color="secondary" @click="offlineLoginFinally()">
Continue
</Button>
</div>
</ModalWrapper>
<ModalWrapper ref="loginErrorModal" class="modal" header="Error while proceed">
<div class="modal-body">
<div class="label">Error occurred while adding offline account</div>
<Button color="primary" @click="retryOfflineLogin()">
Try again
</Button>
</div>
</ModalWrapper>
<ModalWrapper ref="unexpectedErrorModal" class="modal" header="Ошибка">
<div class="modal-body">
<div class="label">Unexcepted error</div>
</div>
</ModalWrapper>
</template>
<script setup>
import { PlusIcon, TrashIcon, LogInIcon } from '@modrinth/assets'
import { PlusIcon, TrashIcon, LogInIcon, PirateIcon as Offline, MicrosoftIcon as License, MicrosoftIcon, PirateIcon } from '@modrinth/assets'
import { Avatar, Button, Card } from '@modrinth/ui'
import { ref, computed, onMounted, onBeforeUnmount, onUnmounted } from 'vue'
import {
offline_login,
users,
remove_user,
set_default_user,
@@ -73,7 +94,7 @@ import { handleError } from '@/store/state.js'
import { trackEvent } from '@/helpers/analytics'
import { process_listener } from '@/helpers/events'
import { handleSevereError } from '@/store/error.js'
import { show_ads_window, hide_ads_window } from '@/helpers/ads.js'
import ModalWrapper from './modal/ModalWrapper.vue'
defineProps({
mode: {
@@ -87,6 +108,46 @@ const emit = defineEmits(['change'])
const accounts = ref({})
const defaultUser = ref()
const loginOfflineModal = ref(null)
const loginErrorModal = ref(null)
const unexpectedErrorModal = ref(null)
const playerName = ref('')
async function tryOfflineLogin() { // Patched
loginOfflineModal.value.show()
}
async function offlineLoginFinally() { // Patched
let name = playerName.value
if (name.length > 1 && name.length < 20 && name !== '') {
const loggedIn = await offline_login(name).catch(handleError)
loginOfflineModal.value.hide()
if (loggedIn) {
await setAccount(loggedIn)
await refreshValues()
} else {
unexpectedErrorModal.value.show()
}
playerName.value = ''
} else {
playerName.value = ''
loginOfflineModal.value.hide()
loginErrorModal.value.show()
}
}
function retryOfflineLogin() { // Patched
loginErrorModal.value.hide()
tryOfflineLogin()
}
function getAccountType(account) { // Patched
if (account.access_token != "null" && account.access_token != null && account.access_token != "") {
return License
} else {
return Offline
}
}
async function refreshValues() {
defaultUser.value = await get_default_user().catch(handleError)
@@ -151,13 +212,8 @@ const handleClickOutside = (event) => {
function toggleMenu(override = true) {
if (showCard.value || !override) {
if (showCard.value) {
show_ads_window()
}
showCard.value = false
} else {
hide_ads_window()
showCard.value = true
}
}
@@ -189,12 +245,18 @@ onUnmounted(() => {
gap: 1rem;
}
.logged-out {
.login-section {
background: var(--color-bg);
border-radius: var(--radius-lg);
gap: 1rem;
}
.vector-icon {
width: 12px;
height: 12px;
}
.account {
width: max-content;
display: flex;
@@ -255,6 +317,12 @@ onUnmounted(() => {
font-weight: bolder;
}
.centered {
display: flex;
gap: 1rem;
margin: auto;
}
.account-group {
width: 100%;
display: flex;

View File

@@ -1,21 +1,13 @@
<template>
<transition name="fade">
<div
v-show="shown"
ref="contextMenu"
class="context-menu"
:style="{
left: left,
top: top,
}"
>
<div v-show="shown" ref="contextMenu" class="context-menu" :style="{
left: left,
top: top,
}">
<div v-for="(option, index) in options" :key="index" @click.stop="optionClicked(option.name)">
<hr v-if="option.type === 'divider'" class="divider" />
<div
v-else-if="!(isLinkedData(item) && option.name === `add_content`)"
class="item clickable"
:class="[option.color ?? 'base']"
>
<div v-else-if="!(isLinkedData(item) && option.name === `add_content`)" class="item clickable"
:class="[option.color ?? 'base']">
<slot :name="option.name" />
</div>
</div>
@@ -25,7 +17,6 @@
<script setup>
import { onBeforeUnmount, onMounted, ref } from 'vue'
import { show_ads_window, hide_ads_window } from '@/helpers/ads.js'
const emit = defineEmits(['menu-closed', 'option-clicked'])
@@ -38,7 +29,6 @@ const shown = ref(false)
defineExpose({
showMenu: (event, passedItem, passedOptions) => {
hide_ads_window()
item.value = passedItem
options.value = passedOptions
@@ -71,9 +61,6 @@ const isLinkedData = (item) => {
}
const hideContextMenu = () => {
if (shown.value) {
show_ads_window()
}
shown.value = false
emit('menu-closed')
}

View File

@@ -1,121 +0,0 @@
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { get as getCreds } from '@/helpers/mr_auth.js'
import { handleError } from '@/store/notifications.js'
import { get_user } from '@/helpers/cache.js'
import { ChevronRightIcon } from '@modrinth/assets'
import { init_ads_window } from '@/helpers/ads.js'
import { listen } from '@tauri-apps/api/event'
const showAd = ref(true)
defineExpose({
scroll() {
updateAdPosition()
},
})
const creds = await getCreds().catch(handleError)
if (creds && creds.user_id) {
const user = await get_user(creds.user_id).catch(handleError)
const MIDAS_BITFLAG = 1 << 0
if (user && (user.badges & MIDAS_BITFLAG) === MIDAS_BITFLAG) {
showAd.value = false
}
}
const adsWrapper = ref(null)
let resizeObserver
let scrollHandler
let intersectionObserver
let mutationObserver
onMounted(() => {
if (showAd.value) {
updateAdPosition(true)
resizeObserver = new ResizeObserver(() => updateAdPosition())
resizeObserver.observe(adsWrapper.value)
intersectionObserver = new IntersectionObserver(() => updateAdPosition())
intersectionObserver.observe(adsWrapper.value)
mutationObserver = new MutationObserver(() => updateAdPosition())
mutationObserver.observe(adsWrapper.value, { attributes: true, childList: true, subtree: true })
// Add scroll event listener
scrollHandler = () => {
requestAnimationFrame(() => updateAdPosition())
}
window.addEventListener('scroll', scrollHandler, { passive: true })
}
})
function updateAdPosition(overrideShown = false) {
if (adsWrapper.value) {
const rect = adsWrapper.value.getBoundingClientRect()
let y = rect.top + window.scrollY
let height = rect.bottom - rect.top
// Prevent ad from overlaying the app bar
if (y <= 52) {
y = 52
height = rect.bottom - 52
if (height < 0) {
height = 0
y = -1000
}
}
init_ads_window(rect.left + window.scrollX, y, rect.right - rect.left, height, overrideShown)
}
}
const unlisten = await listen('ads-scroll', (event) => {
if (adsWrapper.value) {
adsWrapper.value.parentNode.scrollTop += event.payload.scroll
updateAdPosition()
}
})
onUnmounted(() => {
if (resizeObserver) {
resizeObserver.disconnect()
}
if (intersectionObserver) {
intersectionObserver.disconnect()
}
if (mutationObserver) {
mutationObserver.disconnect()
}
if (scrollHandler) {
window.removeEventListener('scroll', scrollHandler)
}
unlisten()
})
</script>
<template>
<div
v-if="showAd"
ref="adsWrapper"
class="ad-parent relative mb-3 flex w-full justify-center rounded-2xl bg-bg-raised cursor-pointer"
>
<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">90% of ad revenue goes to creators</p>
<a
href="https://modrinth.com/plus"
class="mt-auto items-center gap-1 text-purple hover:underline"
>
<span>
Support creators and Modrinth ad-free with
<span class="font-bold">Modrinth+</span>
</span>
<ChevronRightIcon class="relative top-[3px] h-5 w-5" />
</a>
</div>
</div>
</template>

View File

@@ -4,13 +4,8 @@
<ChatIcon />
<span> Get support </span>
</a>
<Button
v-if="currentLoadingBars.length > 0"
ref="infoButton"
icon-only
class="icon-button show-card-icon"
@click="toggleCard()"
>
<Button v-if="currentLoadingBars.length > 0" ref="infoButton" icon-only class="icon-button show-card-icon"
@click="toggleCard()">
<DownloadIcon />
</Button>
<div v-if="offline" class="status">
@@ -25,33 +20,19 @@
<router-link :to="`/instance/${encodeURIComponent(selectedProcess.profile.path)}`">
{{ selectedProcess.profile.name }}
</router-link>
<div
v-if="currentProcesses.length > 1"
class="arrow button-base"
:class="{ rotate: showProfiles }"
@click="toggleProfiles()"
>
<div v-if="currentProcesses.length > 1" class="arrow button-base" :class="{ rotate: showProfiles }"
@click="toggleProfiles()">
<DropdownIcon />
</div>
</div>
<Button
v-tooltip="'Stop instance'"
icon-only
class="icon-button stop"
@click="stop(selectedProcess)"
>
<Button v-tooltip="'Stop instance'" icon-only class="icon-button stop" @click="stop(selectedProcess)">
<StopCircleIcon />
</Button>
<Button v-tooltip="'View logs'" icon-only class="icon-button" @click="goToTerminal()">
<TerminalSquareIcon />
</Button>
<Button
v-if="currentLoadingBars.length > 0"
ref="infoButton"
icon-only
class="icon-button show-card-icon"
@click="toggleCard()"
>
<Button v-if="currentLoadingBars.length > 0" ref="infoButton" icon-only class="icon-button show-card-icon"
@click="toggleCard()">
<DownloadIcon />
</Button>
</div>
@@ -59,6 +40,40 @@
<span class="circle stopped" />
<span class="running-text"> No instances running </span>
</div>
<div v-if="updateState">
<a>
<Button class="download" :disabled="installState" @click="confirmUpdating(), getRemote(false, false)">
<DownloadIcon />
{{
installState
? "Downloading new update..."
: "Download new update"
}}
</Button>
</a>
</div>
<ModalWrapper ref="confirmUpdate" :has-to-type="false" header="Request to update the AstralRinth launcher">
<div class="modal-body">
<div class="markdown-body">
<p>
Before updating, make sure that you have saved all running instances and made a backup copy of the instances
that are valuable to you. Remember that the authors of the product are not responsible for the breakdown of
your files, so you should always make copies of them and keep them in a safe place.
</p>
</div>
<span>Version on remote server <p id="releaseData" class="cosmic inline-fix"></p></span>
<span>Version on local device
<p class="cosmic inline-fix">v{{ version }}</p>
</span>
<div class="button-group push-right">
<Button class="download-modal" @click="confirmUpdate.hide()">
Decline</Button>
<Button class="download-modal" @click="approvedUpdating()">
Accept
</Button>
</div>
</div>
</ModalWrapper>
</div>
<transition name="download">
<Card v-if="showCard === true && currentLoadingBars.length > 0" ref="card" class="info-card">
@@ -74,32 +89,14 @@
</Card>
</transition>
<transition name="download">
<Card
v-if="showProfiles === true && currentProcesses.length > 0"
ref="profiles"
class="profile-card"
>
<Button
v-for="process in currentProcesses"
:key="process.uuid"
class="profile-button"
@click="selectProcess(process)"
>
<Card v-if="showProfiles === true && currentProcesses.length > 0" ref="profiles" class="profile-card">
<Button v-for="process in currentProcesses" :key="process.uuid" class="profile-button"
@click="selectProcess(process)">
<div class="text"><span class="circle running" /> {{ process.profile.name }}</div>
<Button
v-tooltip="'Stop instance'"
icon-only
class="icon-button stop"
@click.stop="stop(process)"
>
<Button v-tooltip="'Stop instance'" icon-only class="icon-button stop" @click.stop="stop(process)">
<StopCircleIcon />
</Button>
<Button
v-tooltip="'View logs'"
icon-only
class="icon-button"
@click.stop="goToTerminal(process.profile.path)"
>
<Button v-tooltip="'View logs'" icon-only class="icon-button" @click.stop="goToTerminal(process.profile.path)">
<TerminalSquareIcon />
</Button>
</Button>
@@ -120,6 +117,23 @@ import { handleError } from '@/store/notifications.js'
import { ChatIcon } from '@/assets/icons'
import { get_many } from '@/helpers/profile.js'
import { trackEvent } from '@/helpers/analytics'
import { version } from '../../../package.json'
import { installState, getRemote, updateState } from '@/helpers/update.js'
import ModalWrapper from './modal/ModalWrapper.vue'
const confirmUpdate = ref(null)
const confirmUpdating = async () => {
confirmUpdate.value.show()
}
const approvedUpdating = async () => {
confirmUpdate.value.hide()
await getRemote(true, true)
}
await getRemote(true, false)
const router = useRouter()
const card = ref(null)
@@ -277,6 +291,101 @@ onBeforeUnmount(() => {
</script>
<style scoped lang="scss">
.inline-fix {
display: inline-flex;
margin-top: -2rem;
margin-bottom: -2rem;
//margin-left: 0.3rem;
}
.cosmic {
color: #3e8cde;
text-decoration: none;
text-shadow:
0 0 4px rgba(79, 173, 255, 0.5),
0 0 8px rgba(14, 98, 204, 0.5),
0 0 12px rgba(122, 31, 199, 0.5);
transition: color 0.35s ease;
}
.markdown-body {
:deep(table) {
width: auto;
}
:deep(hr),
:deep(h1),
:deep(h2) {
max-width: max(60rem, 90%);
}
:deep(ul),
:deep(ol) {
margin-left: 2rem;
}
}
.modal-body {
display: flex;
flex-direction: column;
gap: 1rem;
padding: var(--gap-lg);
text-align: left;
.button-group {
display: flex;
justify-content: flex-end;
gap: 0.5rem;
}
strong {
color: var(--color-contrast);
}
}
.download {
color: #3e8cde;
border-radius: var(--radius-md);
border: 1px solid var(--color-button-bg);
// padding: var(--gap-sm) var(--gap-lg);
background-color: rgba(0, 0, 0, 0);
text-decoration: none;
text-shadow:
0 0 4px rgba(79, 173, 255, 0.5),
0 0 8px rgba(14, 98, 204, 0.5),
0 0 12px rgba(122, 31, 199, 0.5);
transition: color 0.35s ease;
display: flex;
flex-direction: row;
align-items: center;
gap: 0.5rem;
}
.download:hover,
.download:focus,
.download:active {
color: #10fae5;
text-shadow: #26065e;
}
.download-modal {
color: #3e8cde;
padding: var(--gap-sm) var(--gap-lg);
text-decoration: none;
text-shadow:
0 0 4px rgba(79, 173, 255, 0.5),
0 0 8px rgba(14, 98, 204, 0.5),
0 0 12px rgba(122, 31, 199, 0.5);
transition: color 0.35s ease;
}
.download-modal:hover,
.download-modal:focus,
.download-modal:active {
color: #10fae5;
text-shadow: #26065e;
}
.action-groups {
display: flex;
flex-direction: row;
@@ -288,6 +397,7 @@ onBeforeUnmount(() => {
transition: transform 0.2s ease-in-out;
display: flex;
align-items: center;
&.rotate {
transform: rotate(180deg);
}
@@ -309,8 +419,10 @@ onBeforeUnmount(() => {
gap: var(--gap-xs);
white-space: nowrap;
overflow: hidden;
-webkit-user-select: none; /* Safari */
-ms-user-select: none; /* IE 10 and IE 11 */
-webkit-user-select: none;
/* Safari */
-ms-user-select: none;
/* IE 10 and IE 11 */
user-select: none;
&.clickable:hover {

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import { ref } from 'vue'
import { ConfirmModal } from '@modrinth/ui'
import { show_ads_window, hide_ads_window } from '@/helpers/ads.js'
import { useTheming } from '@/store/theme.js'
const themeStore = useTheming()
@@ -36,7 +36,6 @@ const modal = ref(null)
defineExpose({
show: () => {
hide_ads_window()
modal.value.show()
},
hide: () => {
@@ -45,25 +44,13 @@ defineExpose({
},
})
function onModalHide() {
show_ads_window()
}
function proceed() {
emit('proceed')
}
</script>
<template>
<ConfirmModal
ref="modal"
:confirmation-text="confirmationText"
:has-to-type="hasToType"
:title="title"
:description="description"
:proceed-label="proceedLabel"
:on-hide="onModalHide"
:noblur="!themeStore.advancedRendering"
@proceed="proceed"
/>
<ConfirmModal ref="modal" :confirmation-text="confirmationText" :has-to-type="hasToType" :title="title"
:description="description" :proceed-label="proceedLabel" :on-hide="onModalHide"
:noblur="!themeStore.advancedRendering" @proceed="proceed" />
</template>

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import { ref } from 'vue'
import { Modal } from '@modrinth/ui'
import { show_ads_window, hide_ads_window } from '@/helpers/ads.js'
import { useTheming } from '@/store/theme.js'
const themeStore = useTheming()
@@ -18,7 +18,7 @@ const props = defineProps({
onHide: {
type: Function,
default() {
return () => {}
return () => { }
},
},
})
@@ -27,7 +27,6 @@ const modal = ref(null)
defineExpose({
show: () => {
hide_ads_window()
modal.value.show()
},
hide: () => {
@@ -37,7 +36,6 @@ defineExpose({
})
function onModalHide() {
show_ads_window()
props.onHide()
}
</script>

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import { ref } from 'vue'
import { ShareModal } from '@modrinth/ui'
import { show_ads_window, hide_ads_window } from '@/helpers/ads.js'
import { useTheming } from '@/store/theme.js'
const themeStore = useTheming()
@@ -33,7 +33,6 @@ const modal = ref(null)
defineExpose({
show: (passedContent) => {
hide_ads_window()
modal.value.show(passedContent)
},
hide: () => {
@@ -41,21 +40,9 @@ defineExpose({
modal.value.hide()
},
})
function onModalHide() {
show_ads_window()
}
</script>
<template>
<ShareModal
ref="modal"
:header="header"
:share-title="shareTitle"
:share-text="shareText"
:link="link"
:open-in-new-tab="openInNewTab"
:on-hide="onModalHide"
:noblur="!themeStore.advancedRendering"
/>
<ShareModal ref="modal" :header="header" :share-title="shareTitle" :share-text="shareText" :link="link"
:open-in-new-tab="openInNewTab" :on-hide="onModalHide" :noblur="!themeStore.advancedRendering" />
</template>

View File

@@ -1,13 +0,0 @@
import { invoke } from '@tauri-apps/api/core'
export async function init_ads_window(x, y, width, height, overrideShown = false) {
return await invoke('plugin:ads|init_ads_window', { x, y, width, height, overrideShown })
}
export async function show_ads_window() {
return await invoke('plugin:ads|show_ads_window')
}
export async function hide_ads_window(reset) {
return await invoke('plugin:ads|hide_ads_window', { reset })
}

View File

@@ -13,6 +13,10 @@ import { invoke } from '@tauri-apps/api/core'
// await authenticate_await_completion()
// }
export async function offline_login(name) {
return await invoke('plugin:auth|offline_login', { name: name })
}
/**
* Authenticate a user with Hydra - part 1.
* This begins the authentication flow quasi-synchronously.

View File

@@ -0,0 +1,176 @@
import { ref } from 'vue'
import { version } from '../../package.json'
import { getArtifact, getOS } from '@/helpers/utils.js'
export const allowState = ref(false)
export const installState = ref(false)
export const updateState = ref(false)
export const latestBetaCommitTruncatedSha = ref('')
export const latestBetaCommitLink = ref('')
export const launcherUrl = 'https://www.astralium.su/get/ar'
const os = ref('')
const releaseLink = `https://api.github.com/repos/DIDIRUS4/AstralRinth/releases/latest`
const branchesLink = `https://api.github.com/repos/DIDIRUS4/AstralRinth/branches`
const failedFetch = [`Failed to fetch remote releases:`, `Failed to fetch remote commits:`]
const localVersion = `v${version}`
const betaBranch = `beta` // Github repository beta branch
const osNames = ['macos', 'windows', 'linux']
const macExtension = `.dmg` // MacOS file type for download
const windowsExtension = `.msi` // Windows file type for download
const blacklistedBuilds = [
`dev`,
`nightly`,
`dirty`,
`dirty-dev`,
`dirty-nightly`,
`dirty_dev`,
`dirty_nightly`,
] // This is blacklisted builds for download. For example, file.startsWith('dev') is not allowed.
/**
* Asynchronously fetches branches and their latest commit information from the specified URLs.
*
* @return {Promise<void>} This function does not return anything directly but updates the latestBetaCommitTruncatedSha and latestBetaCommitLink values.
*/
export async function getBranches() {
fetch(branchesLink)
.then(async (response) => {
if (response.ok) {
response.json().then((data) => {
const branches = data.map((branch) => branch)
branches.forEach((branch) => {
fetch(branch.commit.url).then(async (data) => {
if (data.ok) {
data.json().then((data) => {
const truncatedSha = data.sha.slice(0, 7)
const commitLink = data.html_url
if (branch.name.toLowerCase() == betaBranch) {
latestBetaCommitTruncatedSha.value = truncatedSha
latestBetaCommitLink.value = commitLink
}
})
} else {
throw new Error(data.status)
}
})
})
})
} else {
throw new Error(response.status)
}
})
.catch((error) => {
latestBetaCommitTruncatedSha.value = error.message
latestBetaCommitLink.value = undefined
console.error(failedFetch[1], error)
})
}
/**
* Asynchronous function to get remote data and handle updates and downloads.
*
* @param {boolean} elementIdBool - Indicates whether to disable an element ID.
* @param {boolean} downloadArtifactBool - Indicates whether to download an artifact.
*/
export async function getRemote(elementIdBool, downloadArtifactBool) {
fetch(releaseLink)
.then((response) => {
if (!response.ok) {
throw new Error(response.status)
}
return response.json()
})
.then(async (data) => {
os.value = await getOS()
const latestRelease = data.name
let remoteVersion = undefined
if (!elementIdBool) {
const releaseData = document.getElementById('releaseData')
if (releaseData == null) {
console.error('Release data element not found.')
return false
}
releaseData.textContent = latestRelease
remoteVersion = `${releaseData.textContent}`
} else {
remoteVersion = latestRelease
}
if (osNames.includes(os.value.toLowerCase())) {
if (remoteVersion.startsWith(localVersion)) {
updateState.value = false
allowState.value = false
} else {
updateState.value = true
allowState.value = true
}
} else {
updateState.value = false
allowState.value = false
}
console.log('Update available state is', updateState.value)
console.log('Remote version is', remoteVersion)
console.log('Local version is', localVersion)
console.log('Operating System is', os.value)
if (downloadArtifactBool) {
installState.value = true
const builds = data.assets
const fileName = getInstaller(getExtension(), builds)
if (fileName != null) {
await getArtifact(fileName[1], fileName[0], os.value, true)
}
installState.value = false
}
})
.catch((error) => {
console.error(failedFetch[0], error)
if (!elementIdBool) {
const errorData = document.getElementById('releaseData')
if (errorData) {
errorData.textContent = `${error.message}`
}
updateState.value = false
allowState.value = false
installState.value = false
}
})
}
/**
* Retrieves the installer for a specific operating system.
*
* @param {string} osExtension - The file extension of the installer.
* @param {Array} builds - The list of builds.
* @return {Array|null} An array containing the installer name and URL if found, or null if not found.
*/
function getInstaller(osExtension, builds) {
for (let i of builds) {
let blacklistedItem = false
blacklistedBuilds.forEach((item) => {
if (i.name.startsWith(item)) {
return (blacklistedItem = true)
}
})
if (i.name.endsWith(osExtension) && !blacklistedItem) {
console.log(i.browser_download_url)
return [i.name, i.browser_download_url]
}
}
return null
}
/**
* A function to get the extension based on the operating system.
*
* @return {string} The extension based on the operating system.
*/
function getExtension() {
if (os.value.toLowerCase() == osNames[0]) {
return macExtension
} else if (os.value.toLowerCase() == osNames[1]) {
return windowsExtension
}
return null
}

View File

@@ -10,6 +10,11 @@ export async function getOS() {
return await invoke('plugin:utils|get_os')
}
export async function getArtifact(downloadurl, filename, ostype, autoupdatesupported) {
console.log('Downloading build', downloadurl, filename, ostype, autoupdatesupported)
return await invoke('plugin:utils|get_artifact', { downloadurl, filename, ostype, autoupdatesupported })
}
export async function openPath(path) {
return await invoke('plugin:utils|open_path', { path })
}

View File

@@ -22,7 +22,6 @@ import { get as getInstance, get_projects as getInstanceProjects } from '@/helpe
import { convertFileSrc } from '@tauri-apps/api/core'
import { get_search_results } from '@/helpers/cache.js'
import { debounce } from '@/helpers/utils.js'
import PromotionWrapper from '@/components/ui/PromotionWrapper.vue'
const router = useRouter()
const route = useRoute()
@@ -529,7 +528,6 @@ const isModProject = computed(() => ['modpack', 'mod'].includes(projectType.valu
<template>
<div ref="searchWrapper" class="search-container">
<aside class="filter-panel" @scroll="$refs.promo.scroll()">
<PromotionWrapper ref="promo" />
<Card v-if="instanceContext" class="small-instance">
<router-link :to="`/instance/${encodeURIComponent(instanceContext.path)}`" class="instance">
<Avatar

View File

@@ -1,5 +1,5 @@
<script setup>
import { ref, onMounted, onUnmounted, 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'
@@ -8,11 +8,6 @@ import { useBreadcrumbs } from '@/store/breadcrumbs'
import { handleError } from '@/store/notifications.js'
import dayjs from 'dayjs'
import { get_search_results } from '@/helpers/cache.js'
import { hide_ads_window } from '@/helpers/ads.js'
onMounted(() => {
hide_ads_window(true)
})
const featuredModpacks = ref({})
const featuredMods = ref({})
@@ -105,31 +100,27 @@ onUnmounted(() => {
<template>
<div class="page-container">
<RowDisplay
v-if="total > 0"
:instances="[
{
label: 'Jump back in',
route: '/library',
instances: recentInstances,
instance: true,
downloaded: true,
},
{
label: 'Popular packs',
route: '/browse/modpack',
instances: featuredModpacks,
downloaded: false,
},
{
label: 'Popular mods',
route: '/browse/mod',
instances: featuredMods,
downloaded: false,
},
]"
:can-paginate="true"
/>
<RowDisplay v-if="total > 0" :instances="[
{
label: 'Jump back in',
route: '/library',
instances: recentInstances,
instance: true,
downloaded: true,
},
{
label: 'Popular packs',
route: '/browse/modpack',
instances: featuredModpacks,
downloaded: false,
},
{
label: 'Popular mods',
route: '/browse/mod',
instances: featuredMods,
downloaded: false,
},
]" :can-paginate="true" />
</div>
</template>

View File

@@ -1,5 +1,5 @@
<script setup>
import { onMounted, onUnmounted, ref, shallowRef } from 'vue'
import { onUnmounted, ref, shallowRef } from 'vue'
import GridDisplay from '@/components/GridDisplay.vue'
import { list } from '@/helpers/profile.js'
import { useRoute } from 'vue-router'
@@ -10,11 +10,6 @@ import { Button } from '@modrinth/ui'
import { PlusIcon } from '@modrinth/assets'
import InstanceCreationModal from '@/components/ui/InstanceCreationModal.vue'
import { NewInstanceImage } from '@/assets/icons'
import { hide_ads_window } from '@/helpers/ads.js'
onMounted(() => {
hide_ads_window(true)
})
const route = useRoute()
const breadcrumbs = useBreadcrumbs()

View File

@@ -1,6 +1,6 @@
<script setup>
import { ref, watch, onMounted } from 'vue'
import { LogOutIcon, LogInIcon, BoxIcon, FolderSearchIcon, TrashIcon } from '@modrinth/assets'
import { ref, watch } from 'vue'
import { LogOutIcon, LogInIcon, BoxIcon, FolderSearchIcon, TrashIcon, PirateShipIcon, UpdatedIcon } from '@modrinth/assets'
import { Card, Slider, DropdownSelect, Toggle, Button } from '@modrinth/ui'
import { handleError, useTheming } from '@/store/state'
import { get, set } from '@/helpers/settings'
@@ -8,24 +8,27 @@ import { get_java_versions, get_max_memory, set_java_version } from '@/helpers/j
import { get as getCreds, logout } from '@/helpers/mr_auth.js'
import JavaSelector from '@/components/ui/JavaSelector.vue'
import ModrinthLoginScreen from '@/components/ui/tutorial/ModrinthLoginScreen.vue'
import { optOutAnalytics, optInAnalytics } from '@/helpers/analytics'
import { optOutAnalytics } from '@/helpers/analytics'
import { open } from '@tauri-apps/plugin-dialog'
import { getOS } from '@/helpers/utils.js'
import { getVersion } from '@tauri-apps/api/app'
// import { getVersion } from '@tauri-apps/api/app'
import { get_user, purge_cache_types } from '@/helpers/cache.js'
import { hide_ads_window } from '@/helpers/ads.js'
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
onMounted(() => {
hide_ads_window()
})
import { version, development_build } from '../../package.json'
import {
getRemote,
getBranches,
launcherUrl,
latestBetaCommitLink,
latestBetaCommitTruncatedSha,
} from '@/helpers/update.js'
const pageOptions = ['Home', 'Library']
const themeStore = useTheming()
const version = await getVersion()
const accessSettings = async () => {
const settings = await get()
@@ -51,7 +54,7 @@ watch(
const setSettings = JSON.parse(JSON.stringify(newSettings))
if (setSettings.telemetry) {
optInAnalytics()
// optInAnalytics()
} else {
optOutAnalytics()
}
@@ -147,6 +150,9 @@ async function purgeCache() {
'search_results',
]).catch(handleError)
}
await getRemote(false, false)
await getBranches()
</script>
<template>
@@ -175,14 +181,9 @@ async function purgeCache() {
Sign in
</button>
</div>
<ConfirmModalWrapper
ref="purgeCacheConfirmModal"
title="Are you sure you want to purge the cache?"
<ConfirmModalWrapper ref="purgeCacheConfirmModal" title="Are you sure you want to purge the cache?"
description="If you proceed, your entire cache will be purged. This may slow down the app temporarily."
:has-to-type="false"
proceed-label="Purge cache"
@proceed="purgeCache"
/>
:has-to-type="false" proceed-label="Purge cache" @proceed="purgeCache" />
<div class="adjacent-input">
<label for="purge-cache">
<span class="label__title">App cache</span>
@@ -225,20 +226,12 @@ async function purgeCache() {
<span class="label__title">Color theme</span>
<span class="label__description">Change the global launcher color theme.</span>
</label>
<DropdownSelect
id="theme"
name="Theme dropdown"
:options="themeStore.themeOptions"
:default-value="settings.theme"
:model-value="settings.theme"
class="theme-dropdown"
@change="
(e) => {
<DropdownSelect id="theme" name="Theme dropdown" :options="themeStore.themeOptions"
:default-value="settings.theme" :model-value="settings.theme" class="theme-dropdown" @change="(e) => {
themeStore.setThemeState(e.option.toLowerCase())
settings.theme = themeStore.selectedTheme
}
"
/>
" />
</div>
<div class="adjacent-input">
<label for="advanced-rendering">
@@ -248,70 +241,45 @@ async function purgeCache() {
without hardware-accelerated rendering.
</span>
</label>
<Toggle
id="advanced-rendering"
:model-value="themeStore.advancedRendering"
:checked="themeStore.advancedRendering"
@update:model-value="
(e) => {
<Toggle id="advanced-rendering" :model-value="themeStore.advancedRendering"
:checked="themeStore.advancedRendering" @update:model-value="(e) => {
themeStore.advancedRendering = e
settings.advanced_rendering = themeStore.advancedRendering
}
"
/>
" />
</div>
<div class="adjacent-input">
<label for="minimize-launcher">
<span class="label__title">Minimize launcher</span>
<span class="label__description"
>Minimize the launcher when a Minecraft process starts.</span
>
<span class="label__description">Minimize the launcher when a Minecraft process starts.</span>
</label>
<Toggle
id="minimize-launcher"
:model-value="settings.hide_on_process_start"
:checked="settings.hide_on_process_start"
@update:model-value="
(e) => {
<Toggle id="minimize-launcher" :model-value="settings.hide_on_process_start"
:checked="settings.hide_on_process_start" @update:model-value="(e) => {
settings.hide_on_process_start = e
}
"
/>
" />
</div>
<div v-if="getOS() != 'MacOS'" class="adjacent-input">
<label for="native-decorations">
<span class="label__title">Native decorations</span>
<span class="label__description">Use system window frame (app restart required).</span>
</label>
<Toggle
id="native-decorations"
:model-value="settings.native_decorations"
:checked="settings.native_decorations"
@update:model-value="
(e) => {
<Toggle id="native-decorations" :model-value="settings.native_decorations"
:checked="settings.native_decorations" @update:model-value="(e) => {
settings.native_decorations = e
}
"
/>
" />
</div>
<div class="adjacent-input">
<label for="opening-page">
<span class="label__title">Default landing page</span>
<span class="label__description">Change the page to which the launcher opens on.</span>
</label>
<DropdownSelect
id="opening-page"
name="Opening page dropdown"
:options="pageOptions"
:default-value="settings.default_page"
:model-value="settings.default_page"
class="opening-page"
@change="
(e) => {
<DropdownSelect id="opening-page" name="Opening page dropdown" :options="pageOptions"
:default-value="settings.default_page" :model-value="settings.default_page" class="opening-page" @change="(e) => {
settings.default_page = e.option
}
"
/>
" />
</div>
</Card>
<Card>
@@ -330,13 +298,7 @@ async function purgeCache() {
effect)
</span>
</label>
<Slider
id="max-downloads"
v-model="settings.max_concurrent_downloads"
:min="1"
:max="10"
:step="1"
/>
<Slider id="max-downloads" v-model="settings.max_concurrent_downloads" :min="1" :max="10" :step="1" />
</div>
<div class="adjacent-input">
@@ -348,13 +310,7 @@ async function purgeCache() {
effect)
</span>
</label>
<Slider
id="max-writes"
v-model="settings.max_concurrent_writes"
:min="1"
:max="50"
:step="1"
/>
<Slider id="max-writes" v-model="settings.max_concurrent_writes" :min="1" :max="50" :step="1" />
</div>
</Card>
<Card>
@@ -367,21 +323,16 @@ async function purgeCache() {
<label for="opt-out-analytics">
<span class="label__title">Telemetry</span>
<span class="label__description">
Modrinth collects anonymized analytics and usage data to improve our user experience and
(Always disabled by AstralRinth) • Modrinth collects anonymized analytics and usage data to improve our user experience and
customize your experience. By disabling this option, you opt out and your data will no
longer be collected.
longer be collected.
</span>
</label>
<Toggle
id="opt-out-analytics"
:model-value="settings.telemetry"
:checked="settings.telemetry"
@update:model-value="
(e) => {
<Toggle id="opt-out-analytics" :model-value="settings.telemetry" :disabled="!settings.telemetry" :checked="settings.telemetry"
@update:model-value="(e) => {
settings.telemetry = e
}
"
/>
" />
</div>
<div class="adjacent-input">
<label for="disable-discord-rpc">
@@ -393,11 +344,7 @@ async function purgeCache() {
mods. (app restart required to take effect)
</span>
</label>
<Toggle
id="disable-discord-rpc"
v-model="settings.discord_rpc"
:checked="settings.discord_rpc"
/>
<Toggle id="disable-discord-rpc" v-model="settings.discord_rpc" :checked="settings.discord_rpc" />
</div>
</Card>
<Card>
@@ -410,36 +357,20 @@ async function purgeCache() {
<label :for="'java-' + version">
<span class="label__title">Java {{ version }} location</span>
</label>
<JavaSelector
:id="'java-selector-' + version"
v-model="javaVersions[version]"
:version="version"
@update:model-value="updateJavaVersion"
/>
<JavaSelector :id="'java-selector-' + version" v-model="javaVersions[version]" :version="version"
@update:model-value="updateJavaVersion" />
</template>
<hr class="card-divider" />
<label for="java-args">
<span class="label__title">Java arguments</span>
</label>
<input
id="java-args"
v-model="settings.launchArgs"
autocomplete="off"
type="text"
class="installation-input"
placeholder="Enter java arguments..."
/>
<input id="java-args" v-model="settings.launchArgs" autocomplete="off" type="text" class="installation-input"
placeholder="Enter java arguments..." />
<label for="env-vars">
<span class="label__title">Environmental variables</span>
</label>
<input
id="env-vars"
v-model="settings.envVars"
autocomplete="off"
type="text"
class="installation-input"
placeholder="Enter environmental variables..."
/>
<input id="env-vars" v-model="settings.envVars" autocomplete="off" type="text" class="installation-input"
placeholder="Enter environmental variables..." />
<hr class="card-divider" />
<div class="adjacent-input">
<label for="max-memory">
@@ -448,14 +379,7 @@ async function purgeCache() {
The memory allocated to each instance when it is ran.
</span>
</label>
<Slider
id="max-memory"
v-model="settings.memory.maximum"
:min="8"
:max="maxMemory"
:step="64"
unit="mb"
/>
<Slider id="max-memory" v-model="settings.memory.maximum" :min="8" :max="maxMemory" :step="64" unit="mb" />
</div>
</Card>
<Card>
@@ -469,39 +393,24 @@ async function purgeCache() {
<span class="label__title">Pre launch</span>
<span class="label__description"> Ran before the instance is launched. </span>
</label>
<input
id="pre-launch"
v-model="settings.hooks.pre_launch"
autocomplete="off"
type="text"
placeholder="Enter pre-launch command..."
/>
<input id="pre-launch" v-model="settings.hooks.pre_launch" autocomplete="off" type="text"
placeholder="Enter pre-launch command..." />
</div>
<div class="adjacent-input">
<label for="wrapper">
<span class="label__title">Wrapper</span>
<span class="label__description"> Wrapper command for launching Minecraft. </span>
</label>
<input
id="wrapper"
v-model="settings.hooks.wrapper"
autocomplete="off"
type="text"
placeholder="Enter wrapper command..."
/>
<input id="wrapper" v-model="settings.hooks.wrapper" autocomplete="off" type="text"
placeholder="Enter wrapper command..." />
</div>
<div class="adjacent-input">
<label for="post-exit">
<span class="label__title">Post exit</span>
<span class="label__description"> Ran after the game closes. </span>
</label>
<input
id="post-exit"
v-model="settings.hooks.post_exit"
autocomplete="off"
type="text"
placeholder="Enter post-exit command..."
/>
<input id="post-exit" v-model="settings.hooks.post_exit" autocomplete="off" type="text"
placeholder="Enter post-exit command..." />
</div>
</Card>
<Card>
@@ -517,58 +426,71 @@ async function purgeCache() {
Overwrites the options.txt file to start in full screen when launched.
</span>
</label>
<Toggle
id="fullscreen"
:model-value="settings.force_fullscreen"
:checked="settings.force_fullscreen"
@update:model-value="
(e) => {
<Toggle id="fullscreen" :model-value="settings.force_fullscreen" :checked="settings.force_fullscreen"
@update:model-value="(e) => {
settings.force_fullscreen = e
}
"
/>
" />
</div>
<div class="adjacent-input">
<label for="width">
<span class="label__title">Width</span>
<span class="label__description"> The width of the game window when launched. </span>
</label>
<input
id="width"
v-model="settings.game_resolution[0]"
:disabled="settings.force_fullscreen"
autocomplete="off"
type="number"
placeholder="Enter width..."
/>
<input id="width" v-model="settings.game_resolution[0]" :disabled="settings.force_fullscreen" autocomplete="off"
type="number" placeholder="Enter width..." />
</div>
<div class="adjacent-input">
<label for="height">
<span class="label__title">Height</span>
<span class="label__description"> The height of the game window when launched. </span>
</label>
<input
id="height"
v-model="settings.game_resolution[1]"
:disabled="settings.force_fullscreen"
autocomplete="off"
type="number"
class="input"
placeholder="Enter height..."
/>
<input id="height" v-model="settings.game_resolution[1]" :disabled="settings.force_fullscreen"
autocomplete="off" type="number" class="input" placeholder="Enter height..." />
</div>
</Card>
<Card>
<div class="label">
<div class="label inline-fix">
<h3>
<span class="label__title size-card-header">About</span>
<span class="label__title size-card-header in"
> About
<p v-if="development_build" class="development option">
You are using a development version, there may be errors.
</p>
</span>
</h3>
</div>
<div>
<label>
<span class="label__title">App version</span>
<span class="label__description">Modrinth App v{{ version }} </span>
<span class="label__title inl">AstralRinth <PirateShipIcon /> Version • {{ version }}</span>
<span class="label__description"
>Latest beta commit •
<a class="github" :href="latestBetaCommitLink">{{
latestBetaCommitTruncatedSha
}}</a></span
>
<span class="label__description"
>All latest versions always published on GitHub
<a class="github" :href="launcherUrl">Our GitHub repository</a></span
>
<span class="label__title">Update Checker</span>
<span class="label__description"
>Version on remote server •
<p id="releaseData" class="cosmic inline-fix"></p>
</span>
<span class="label__description"
>Version on local device •
<p class="cosmic inline-fix">v{{ version }}</p></span
>
</label>
<div class="inline-item-group">
<Button icon-only @click="getRemote(false, false), getBranches()">
<UpdatedIcon /> Check for updates
</Button>
</div>
</div>
</Card>
</div>
@@ -606,4 +528,112 @@ async function purgeCache() {
}
}
}
.development {
color: #ff6a00;
text-decoration: none;
text-shadow:
0 0 4px rgba(79, 173, 255, 0.5),
0 0 8px rgba(14, 98, 204, 0.5),
0 0 12px rgba(122, 31, 199, 0.5);
transition: color 1.5s ease;
}
.development:hover,
.development:focus,
.development:active {
color: #4800d3;
text-shadow: #801313;
}
.cosmic {
color: #3e8cde;
text-decoration: none;
text-shadow:
0 0 4px rgba(79, 173, 255, 0.5),
0 0 8px rgba(14, 98, 204, 0.5),
0 0 12px rgba(122, 31, 199, 0.5);
transition: color 0.35s ease;
}
.cosmic:hover,
.cosmic:focus,
.cosmic:active {
color: #10fae5;
text-shadow: #26065e;
}
.download {
color: #3e8cde;
border: none;
padding: var(--gap-sm) var(--gap-lg);
//background-color: rgba(0, 0, 0, 0.0);
text-decoration: none;
text-shadow:
0 0 4px rgba(79, 173, 255, 0.5),
0 0 8px rgba(14, 98, 204, 0.5),
0 0 12px rgba(122, 31, 199, 0.5);
transition: color 0.35s ease;
}
.download:hover,
.download:focus,
.download:active {
color: #10fae5;
text-shadow: #26065e;
}
a.github {
color: #3e8cde;
text-decoration: none;
text-shadow:
0 0 4px rgba(79, 173, 255, 0.5),
0 0 8px rgba(14, 98, 204, 0.5),
0 0 12px rgba(122, 31, 199, 0.5);
transition: color 0.35s ease;
}
a.github:hover,
a.github:focus,
a.github:active {
color: #10fae5;
text-shadow: #26065e;
}
.inline-item-group {
display: inline-flex;
gap: 0.25rem;
}
.inline-fix {
display: inline-flex;
margin-top: -2rem;
margin-bottom: -2rem;
}
.download-modal {
color: #3e8cde;
padding: var(--gap-sm) var(--gap-lg);
text-decoration: none;
text-shadow:
0 0 4px rgba(79, 173, 255, 0.5),
0 0 8px rgba(14, 98, 204, 0.5),
0 0 12px rgba(122, 31, 199, 0.5);
transition: color 0.35s ease;
}
.download-modal:hover,
.download-modal:focus,
.download-modal:active {
color: #10fae5;
text-shadow: #26065e;
}
.option {
background: var(--color-bg);
border-radius: var(--radius-lg);
width: auto;
display: inline-flex;
align-items: center;
margin-top: auto;
margin-left: 0.5rem;
font-size: 1rem;
padding: 0.5rem;
}
</style>

View File

@@ -61,7 +61,6 @@
</RouterLink>
</div>
</Card>
<PromotionWrapper ref="promo" class="mt-4" />
</div>
<div class="content">
<RouterView v-slot="{ Component }">
@@ -136,7 +135,6 @@ import { convertFileSrc } from '@tauri-apps/api/core'
import { handleSevereError } from '@/store/error.js'
import { get_project, get_version_many } from '@/helpers/cache.js'
import dayjs from 'dayjs'
import PromotionWrapper from '@/components/ui/PromotionWrapper.vue'
const route = useRoute()

View File

@@ -62,7 +62,6 @@
</a>
</div>
</Card>
<PromotionWrapper ref="promo" />
<Card class="sidebar-card">
<div class="stats">
<div class="stat">
@@ -262,7 +261,6 @@ import { convertFileSrc } from '@tauri-apps/api/core'
import ContextMenu from '@/components/ui/ContextMenu.vue'
import { install as installVersion } from '@/store/install.js'
import { get_project, get_project_many, get_team, get_version_many } from '@/helpers/cache.js'
import PromotionWrapper from '@/components/ui/PromotionWrapper.vue'
dayjs.extend(relativeTime)

View File

@@ -6,11 +6,11 @@
<array>
<dict>
<key>CFBundleURLName</key>
<string>ModrinthApp</string>
<string>AstralRinthApp</string>
<key>CFBundleURLSchemes</key>
<array>
<string>modrinth</string>
<string>modrinthscheme</string>
<string>astralrinth</string>
<string>astralrinthscheme</string>
</array>
</dict>
</array>

View File

@@ -12,6 +12,7 @@ fn main() {
"auth",
InlinedPlugin::new()
.commands(&[
"offline_login",
"login",
"remove_user",
"get_default_user",
@@ -206,6 +207,7 @@ fn main() {
"utils",
InlinedPlugin::new()
.commands(&[
"get_artifact",
"get_os",
"should_disable_mouseover",
"highlight_in_folder",
@@ -217,19 +219,6 @@ fn main() {
.default_permission(
DefaultPermissionRule::AllowAllCommands,
),
)
.plugin(
"ads",
InlinedPlugin::new()
.commands(&[
"init_ads_window",
"hide_ads_window",
"scroll_ads_window",
"show_ads_window",
])
.default_permission(
DefaultPermissionRule::AllowAllCommands,
),
),
)
.expect("Failed to run tauri-build");

View File

@@ -1,15 +0,0 @@
{
"identifier": "ads",
"description": "",
"local": false,
"remote": {
"urls": ["https://modrinth.com/*", "http://localhost:3000/*"]
},
"webviews": [
"ads-window"
],
"permissions": [
"shell:allow-open",
"ads:default"
]
}

View File

@@ -35,7 +35,6 @@
"cache:default",
"settings:default",
"tags:default",
"utils:default",
"ads:default"
"utils:default"
]
}

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
{"ads":{"identifier":"ads","description":"","remote":{"urls":["https://modrinth.com/*","http://localhost:3000/*"]},"local":false,"webviews":["ads-window"],"permissions":["shell:allow-open","ads:default"]},"core":{"identifier":"core","description":"","local":true,"windows":["main"],"permissions":["core:default","core:path:default","core:event:default","core:window:default","core:app:default","core:resources:default","core:menu:default","core:tray:default","core:window:allow-create","core:window:allow-maximize","core:window:allow-toggle-maximize","core:window:allow-unmaximize","core:window:allow-minimize","core:window:allow-unminimize","core:window:allow-show","core:window:allow-hide","core:window:allow-close","core:window:allow-set-decorations","core:window:allow-start-dragging","core:webview:allow-set-webview-zoom"]},"plugins":{"identifier":"plugins","description":"","local":true,"windows":["main"],"permissions":["dialog:allow-open","dialog:allow-confirm","shell:allow-open","os:allow-platform","os:allow-version","os:allow-os-type","os:allow-family","os:allow-arch","os:allow-exe-extension","os:allow-locale","os:allow-hostname","deep-link:default","window-state:default","window-state:allow-restore-state","window-state:allow-save-window-state","auth:default","import:default","jre:default","logs:default","metadata:default","mr-auth:default","profile-create:default","pack:default","process:default","profile:default","cache:default","settings:default","tags:default","utils:default","ads:default"]},"updater":{"identifier":"updater","description":"","local":true,"windows":["main"],"permissions":["updater:default"]}}
{"core":{"identifier":"core","description":"","local":true,"windows":["main"],"permissions":["core:default","core:path:default","core:event:default","core:window:default","core:app:default","core:resources:default","core:menu:default","core:tray:default","core:window:allow-create","core:window:allow-maximize","core:window:allow-toggle-maximize","core:window:allow-unmaximize","core:window:allow-minimize","core:window:allow-unminimize","core:window:allow-show","core:window:allow-hide","core:window:allow-close","core:window:allow-set-decorations","core:window:allow-start-dragging","core:webview:allow-set-webview-zoom"]},"plugins":{"identifier":"plugins","description":"","local":true,"windows":["main"],"permissions":["dialog:allow-open","dialog:allow-confirm","shell:allow-open","os:allow-platform","os:allow-version","os:allow-os-type","os:allow-family","os:allow-arch","os:allow-exe-extension","os:allow-locale","os:allow-hostname","deep-link:default","window-state:default","window-state:allow-restore-state","window-state:allow-save-window-state","auth:default","import:default","jre:default","logs:default","metadata:default","mr-auth:default","profile-create:default","pack:default","process:default","profile:default","cache:default","settings:default","tags:default","utils:default"]},"updater":{"identifier":"updater","description":"","local":true,"windows":["main"],"permissions":["updater:default"]}}

View File

@@ -299,69 +299,6 @@
},
"Identifier": {
"oneOf": [
{
"description": "ads:default -> Default plugin permissions.",
"type": "string",
"enum": [
"ads:default"
]
},
{
"description": "ads:allow-hide-ads-window -> Enables the hide_ads_window command without any pre-configured scope.",
"type": "string",
"enum": [
"ads:allow-hide-ads-window"
]
},
{
"description": "ads:allow-init-ads-window -> Enables the init_ads_window command without any pre-configured scope.",
"type": "string",
"enum": [
"ads:allow-init-ads-window"
]
},
{
"description": "ads:allow-scroll-ads-window -> Enables the scroll_ads_window command without any pre-configured scope.",
"type": "string",
"enum": [
"ads:allow-scroll-ads-window"
]
},
{
"description": "ads:allow-show-ads-window -> Enables the show_ads_window command without any pre-configured scope.",
"type": "string",
"enum": [
"ads:allow-show-ads-window"
]
},
{
"description": "ads:deny-hide-ads-window -> Denies the hide_ads_window command without any pre-configured scope.",
"type": "string",
"enum": [
"ads:deny-hide-ads-window"
]
},
{
"description": "ads:deny-init-ads-window -> Denies the init_ads_window command without any pre-configured scope.",
"type": "string",
"enum": [
"ads:deny-init-ads-window"
]
},
{
"description": "ads:deny-scroll-ads-window -> Denies the scroll_ads_window command without any pre-configured scope.",
"type": "string",
"enum": [
"ads:deny-scroll-ads-window"
]
},
{
"description": "ads:deny-show-ads-window -> Denies the show_ads_window command without any pre-configured scope.",
"type": "string",
"enum": [
"ads:deny-show-ads-window"
]
},
{
"description": "auth:default -> Default plugin permissions.",
"type": "string",
@@ -390,6 +327,13 @@
"auth:allow-login"
]
},
{
"description": "auth:allow-offline-login -> Enables the offline_login command without any pre-configured scope.",
"type": "string",
"enum": [
"auth:allow-offline-login"
]
},
{
"description": "auth:allow-remove-user -> Enables the remove_user command without any pre-configured scope.",
"type": "string",
@@ -425,6 +369,13 @@
"auth:deny-login"
]
},
{
"description": "auth:deny-offline-login -> Denies the offline_login command without any pre-configured scope.",
"type": "string",
"enum": [
"auth:deny-offline-login"
]
},
{
"description": "auth:deny-remove-user -> Denies the remove_user command without any pre-configured scope.",
"type": "string",
@@ -3988,6 +3939,13 @@
"utils:default"
]
},
{
"description": "utils:allow-get-artifact -> Enables the get_artifact command without any pre-configured scope.",
"type": "string",
"enum": [
"utils:allow-get-artifact"
]
},
{
"description": "utils:allow-get-opening-command -> Enables the get_opening_command command without any pre-configured scope.",
"type": "string",
@@ -4037,6 +3995,13 @@
"utils:allow-show-launcher-logs-folder"
]
},
{
"description": "utils:deny-get-artifact -> Denies the get_artifact command without any pre-configured scope.",
"type": "string",
"enum": [
"utils:deny-get-artifact"
]
},
{
"description": "utils:deny-get-opening-command -> Denies the get_opening_command command without any pre-configured scope.",
"type": "string",

View File

@@ -299,69 +299,6 @@
},
"Identifier": {
"oneOf": [
{
"description": "ads:default -> Default plugin permissions.",
"type": "string",
"enum": [
"ads:default"
]
},
{
"description": "ads:allow-hide-ads-window -> Enables the hide_ads_window command without any pre-configured scope.",
"type": "string",
"enum": [
"ads:allow-hide-ads-window"
]
},
{
"description": "ads:allow-init-ads-window -> Enables the init_ads_window command without any pre-configured scope.",
"type": "string",
"enum": [
"ads:allow-init-ads-window"
]
},
{
"description": "ads:allow-scroll-ads-window -> Enables the scroll_ads_window command without any pre-configured scope.",
"type": "string",
"enum": [
"ads:allow-scroll-ads-window"
]
},
{
"description": "ads:allow-show-ads-window -> Enables the show_ads_window command without any pre-configured scope.",
"type": "string",
"enum": [
"ads:allow-show-ads-window"
]
},
{
"description": "ads:deny-hide-ads-window -> Denies the hide_ads_window command without any pre-configured scope.",
"type": "string",
"enum": [
"ads:deny-hide-ads-window"
]
},
{
"description": "ads:deny-init-ads-window -> Denies the init_ads_window command without any pre-configured scope.",
"type": "string",
"enum": [
"ads:deny-init-ads-window"
]
},
{
"description": "ads:deny-scroll-ads-window -> Denies the scroll_ads_window command without any pre-configured scope.",
"type": "string",
"enum": [
"ads:deny-scroll-ads-window"
]
},
{
"description": "ads:deny-show-ads-window -> Denies the show_ads_window command without any pre-configured scope.",
"type": "string",
"enum": [
"ads:deny-show-ads-window"
]
},
{
"description": "auth:default -> Default plugin permissions.",
"type": "string",
@@ -390,6 +327,13 @@
"auth:allow-login"
]
},
{
"description": "auth:allow-offline-login -> Enables the offline_login command without any pre-configured scope.",
"type": "string",
"enum": [
"auth:allow-offline-login"
]
},
{
"description": "auth:allow-remove-user -> Enables the remove_user command without any pre-configured scope.",
"type": "string",
@@ -425,6 +369,13 @@
"auth:deny-login"
]
},
{
"description": "auth:deny-offline-login -> Denies the offline_login command without any pre-configured scope.",
"type": "string",
"enum": [
"auth:deny-offline-login"
]
},
{
"description": "auth:deny-remove-user -> Denies the remove_user command without any pre-configured scope.",
"type": "string",
@@ -3988,6 +3939,13 @@
"utils:default"
]
},
{
"description": "utils:allow-get-artifact -> Enables the get_artifact command without any pre-configured scope.",
"type": "string",
"enum": [
"utils:allow-get-artifact"
]
},
{
"description": "utils:allow-get-opening-command -> Enables the get_opening_command command without any pre-configured scope.",
"type": "string",
@@ -4037,6 +3995,13 @@
"utils:allow-show-launcher-logs-folder"
]
},
{
"description": "utils:deny-get-artifact -> Denies the get_artifact command without any pre-configured scope.",
"type": "string",
"enum": [
"utils:deny-get-artifact"
]
},
{
"description": "utils:deny-get-opening-command -> Denies the get_opening_command command without any pre-configured scope.",
"type": "string",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 34 KiB

BIN
apps/app/icons/icon (1).png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 401 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

View File

@@ -1,20 +0,0 @@
if (!window.modrinthClickListener) {
window.modrinthClickListener = true
document.addEventListener('click', function (e) {
let target = e.target
while (target != null) {
if (target.matches('a')) {
e.preventDefault()
if (target.href) {
window.top.postMessage({ modrinthOpenUrl: target.href }, 'https://modrinth.com')
}
break
}
target = target.parentElement
}
})
}
window.open = (url, target, features) => {
window.top.postMessage({ modrinthOpenUrl: url }, 'https://modrinth.com')
}

View File

@@ -1,143 +0,0 @@
use serde::Serialize;
use tauri::plugin::TauriPlugin;
use tauri::{Emitter, LogicalPosition, LogicalSize, Manager, Runtime};
use tokio::sync::RwLock;
pub struct AdsState {
pub shown: bool,
pub size: Option<LogicalSize<f32>>,
pub position: Option<LogicalPosition<f32>>,
}
pub fn init<R: Runtime>() -> TauriPlugin<R> {
tauri::plugin::Builder::<R>::new("ads")
.setup(|app, _api| {
app.manage(RwLock::new(AdsState {
shown: true,
size: None,
position: None,
}));
Ok(())
})
.invoke_handler(tauri::generate_handler![
init_ads_window,
hide_ads_window,
scroll_ads_window,
show_ads_window,
])
.build()
}
#[tauri::command]
#[cfg(not(target_os = "linux"))]
pub async fn init_ads_window<R: Runtime>(
app: tauri::AppHandle<R>,
x: f32,
y: f32,
width: f32,
height: f32,
override_shown: bool,
) -> crate::api::Result<()> {
use tauri::WebviewUrl;
const LINK_SCRIPT: &str = include_str!("ads-init.js");
let state = app.state::<RwLock<AdsState>>();
let mut state = state.write().await;
state.size = Some(LogicalSize::new(width, height));
state.position = Some(LogicalPosition::new(x, y));
if override_shown {
state.shown = true;
}
if let Some(webview) = app.webviews().get("ads-window") {
if state.shown {
let _ = webview.set_position(LogicalPosition::new(x, y));
let _ = webview.set_size(LogicalSize::new(width, height));
}
} else if let Some(window) = app.get_window("main") {
let _ = window.add_child(
tauri::webview::WebviewBuilder::new(
"ads-window",
WebviewUrl::External(
"https://modrinth.com/wrapper/app-ads".parse().unwrap(),
),
)
.initialization_script(LINK_SCRIPT)
.user_agent("ModrinthApp Ads Webview")
.zoom_hotkeys_enabled(false)
.transparent(true),
if state.shown {
LogicalPosition::new(x, y)
} else {
LogicalPosition::new(-1000.0, -1000.0)
},
LogicalSize::new(width, height),
);
}
Ok(())
}
// TODO: make ads work on linux
#[tauri::command]
#[cfg(target_os = "linux")]
pub async fn init_ads_window() {}
#[tauri::command]
pub async fn show_ads_window<R: Runtime>(
app: tauri::AppHandle<R>,
) -> crate::api::Result<()> {
if let Some(webview) = app.webviews().get("ads-window") {
let state = app.state::<RwLock<AdsState>>();
let mut state = state.write().await;
state.shown = true;
if let Some(size) = state.size {
let _ = webview.set_size(size);
}
if let Some(position) = state.position {
let _ = webview.set_position(position);
}
}
Ok(())
}
#[tauri::command]
pub async fn hide_ads_window<R: Runtime>(
app: tauri::AppHandle<R>,
reset: Option<bool>,
) -> crate::api::Result<()> {
if let Some(webview) = app.webviews().get("ads-window") {
let state = app.state::<RwLock<AdsState>>();
let mut state = state.write().await;
state.shown = false;
if reset.unwrap_or(false) {
state.size = None;
state.position = None;
}
let _ = webview.set_position(LogicalPosition::new(-1000, -1000));
}
Ok(())
}
#[derive(Serialize, Clone)]
struct ScrollEvent {
scroll: f32,
}
#[tauri::command]
pub async fn scroll_ads_window<R: Runtime>(
app: tauri::AppHandle<R>,
scroll: f32,
) -> crate::api::Result<()> {
let _ = app.emit("ads-scroll", ScrollEvent { scroll });
Ok(())
}

View File

@@ -7,6 +7,7 @@ use theseus::prelude::*;
pub fn init<R: Runtime>() -> TauriPlugin<R> {
tauri::plugin::Builder::<R>::new("auth")
.invoke_handler(tauri::generate_handler![
offline_login,
login,
remove_user,
get_default_user,
@@ -16,6 +17,14 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
.build()
}
/// Create new offline user
/// This is custom function from Astralium Org.
#[tauri::command]
pub async fn offline_login(name: &str) -> Result<Credentials> {
let credentials = minecraft_auth::offline_auth(name).await?;
Ok(credentials)
}
/// Authenticate a user with Hydra - part 1
/// This begins the authentication flow quasi-synchronously, returning a URL to visit (that the user will sign in at)
#[tauri::command]

View File

@@ -16,7 +16,6 @@ pub mod settings;
pub mod tags;
pub mod utils;
pub mod ads;
pub mod cache;
pub type Result<T> = std::result::Result<T, TheseusSerializableError>;

View File

@@ -11,6 +11,7 @@ use std::path::PathBuf;
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
tauri::plugin::Builder::new("utils")
.invoke_handler(tauri::generate_handler![
get_artifact,
get_os,
should_disable_mouseover,
highlight_in_folder,
@@ -22,6 +23,12 @@ pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
.build()
}
#[tauri::command]
pub async fn get_artifact(downloadurl: &str, filename: &str, ostype: &str, autoupdatesupported: bool) -> Result<()> {
theseus::download::init_download(downloadurl, filename, ostype, autoupdatesupported).await;
Ok(())
}
/// Gets OS
#[tauri::command]
pub fn get_os() -> OS {

View File

@@ -27,61 +27,61 @@ extern crate objc;
async fn initialize_state(app: tauri::AppHandle) -> api::Result<()> {
theseus::EventState::init(app.clone()).await?;
#[cfg(feature = "updater")]
{
use tauri_plugin_updater::UpdaterExt;
// #[cfg(feature = "updater")]
// {
// use tauri_plugin_updater::UpdaterExt;
let updater = app.updater_builder().build()?;
// let updater = app.updater_builder().build()?;
let update_fut = updater.check();
// let update_fut = updater.check();
State::init().await?;
// State::init().await?;
let check_bar = theseus::init_loading(
theseus::LoadingBarType::CheckingForUpdates,
1.0,
"Checking for updates...",
)
.await?;
// let check_bar = theseus::init_loading(
// theseus::LoadingBarType::CheckingForUpdates,
// 1.0,
// "Checking for updates...",
// )
// .await?;
let update = update_fut.await;
// let update = update_fut.await;
drop(check_bar);
// drop(check_bar);
if let Some(update) = update.ok().flatten() {
tracing::info!("Update found: {:?}", update.download_url);
let loader_bar_id = theseus::init_loading(
theseus::LoadingBarType::LauncherUpdate {
version: update.version.clone(),
current_version: update.current_version.clone(),
},
1.0,
"Updating Modrinth App...",
)
.await?;
// if let Some(update) = update.ok().flatten() {
// tracing::info!("Update found: {:?}", update.download_url);
// let loader_bar_id = theseus::init_loading(
// theseus::LoadingBarType::LauncherUpdate {
// version: update.version.clone(),
// current_version: update.current_version.clone(),
// },
// 1.0,
// "Updating Modrinth App...",
// )
// .await?;
// 100 MiB
const DEFAULT_CONTENT_LENGTH: u64 = 1024 * 1024 * 100;
// // 100 MiB
// const DEFAULT_CONTENT_LENGTH: u64 = 1024 * 1024 * 100;
update
.download_and_install(
|chunk_length, content_length| {
let _ = theseus::emit_loading(
&loader_bar_id,
(chunk_length as f64)
/ (content_length
.unwrap_or(DEFAULT_CONTENT_LENGTH)
as f64),
None,
);
},
|| {},
)
.await?;
// update
// .download_and_install(
// |chunk_length, content_length| {
// let _ = theseus::emit_loading(
// &loader_bar_id,
// (chunk_length as f64)
// / (content_length
// .unwrap_or(DEFAULT_CONTENT_LENGTH)
// as f64),
// None,
// );
// },
// || {},
// )
// .await?;
app.restart();
}
}
// app.restart();
// }
// }
#[cfg(not(feature = "updater"))]
{
@@ -259,7 +259,6 @@ fn main() {
.plugin(api::tags::init())
.plugin(api::utils::init())
.plugin(api::cache::init())
.plugin(api::ads::init())
.invoke_handler(tauri::generate_handler![
initialize_state,
is_dev,

View File

@@ -7,7 +7,7 @@
},
"app": {
"security": {
"capabilities": ["ads", "core", "plugins", "updater"]
"capabilities": ["core", "plugins", "updater"]
}
},
"plugins": {

View File

@@ -47,9 +47,9 @@
}
]
},
"productName": "Modrinth App",
"productName": "AstralRinth App",
"version": "0.8.5",
"identifier": "ModrinthApp",
"identifier": "AstralRinth",
"plugins": {
"deep-link": {
"desktop": {
@@ -68,7 +68,7 @@
"fullscreen": false,
"height": 800,
"resizable": true,
"title": "Modrinth App",
"title": "AstralRinth",
"width": 1280,
"minHeight": 750,
"minWidth": 1100,
@@ -86,10 +86,10 @@
],
"enable": true
},
"capabilities": ["ads", "core", "plugins"],
"capabilities": ["core", "plugins"],
"csp": {
"default-src": "'self' customprotocol: asset:",
"connect-src": "ipc: http://ipc.localhost https://modrinth.com https://*.modrinth.com https://*.posthog.com https://*.sentry.io https://*.cloudflare.com https://api.mclo.gs https://cmp.inmobi.com",
"connect-src": "https://api.github.com ipc: http://ipc.localhost https://modrinth.com https://*.modrinth.com https://*.posthog.com https://*.sentry.io https://*.cloudflare.com https://api.mclo.gs https://cmp.inmobi.com",
"font-src": [
"https://cdn-raw.modrinth.com/fonts/inter/"
],

View File

@@ -7,7 +7,7 @@
"fullscreen": false,
"height": 800,
"resizable": true,
"title": "Modrinth App",
"title": "AstralRinth",
"width": 1280,
"minHeight": 700,
"minWidth": 1100,