feat(instance-sync): screenshots sync (#7218)

* pnpm prepr

* feat(instance-sync): screenshots sync

* fix: prepr + fmt

* fix: qa

* feat: screenshit editor

* feat: screenshot* editor qa

* fix: qa

* fix: lint

* fix: aecsocket rev

* qa: a11y tab navigation focus is cut off

* qa: Change “Edited” badge to be bg-highlight-green

* qa: friends list input style wrong

* qa: toolbar width + labels

* qa: multiselect changes

* qa: anim changes

* qa: card hover colors

* qa: copy feedback

* qa: hide date

* qa: instance icon in overflow/contextmenu

* qa: spacing

* qa: group empty state text

* qa: drag preview badge

* qa: group deletion skip warning if no screenshots

* qa: redesign editor

* qa: remove screenshot parent/edited badge stuff

* qa: control font size consistency

* qa: viewer copy control

* qa: editor fixes

* qa: redirect on disable screenshot sync

* qa: margin police

* fix: crop

* fix: qa

* feat: initial start on basic instance file syncing (#7220)

* qa: final

* qa: final 2

* fix: lint + prepr

* fix: copy

* fix: screenshot editing outside of app causing ghost files in db + sync fail rollback impl

* chore: split up

* fix: fmt

---------

Co-authored-by: tdgao <mr.trumgao@gmail.com>
This commit is contained in:
Calum H.
2026-08-27 16:49:46 +00:00
committed by GitHub
co-authored by tdgao
parent 2bd108c278
commit 7c67cca7a7
217 changed files with 17139 additions and 1887 deletions
+47 -2
View File
@@ -14,6 +14,7 @@ import {
ChevronLeftIcon,
ChevronRightIcon,
CompassIcon,
ImagesIcon,
LogInIcon,
LogOutIcon,
NewspaperIcon,
@@ -64,6 +65,7 @@ import { renderString } from '@modrinth/utils'
import { useQuery, useQueryClient } from '@tanstack/vue-query'
import { getVersion } from '@tauri-apps/api/app'
import { convertFileSrc, invoke } from '@tauri-apps/api/core'
import { listen } from '@tauri-apps/api/event'
import { getCurrentWindow } from '@tauri-apps/api/window'
import { fetch as tauriFetch } from '@tauri-apps/plugin-http'
import { openUrl } from '@tauri-apps/plugin-opener'
@@ -118,7 +120,12 @@ import { debugAnalytics, initAnalytics, trackEvent } from '@/helpers/analytics'
import { check_reachable } from '@/helpers/auth.js'
import { get_user, get_user_many, get_version } from '@/helpers/cache.js'
import { install_create_modpack_instance, install_get_modpack_preview } from '@/helpers/install'
import { can_current_user_use_shared_instances, get as getInstance, run } from '@/helpers/instance'
import {
can_current_user_use_shared_instances,
get as getInstance,
get_global_synced_options,
run,
} from '@/helpers/instance'
import {
get as getCreds,
getAll as getAllCreds,
@@ -173,7 +180,10 @@ import { generateSkinPreviews } from './helpers/rendering/batch-skin-renderer'
import { get_available_capes, get_available_skins } from './helpers/skins'
import { AppNotificationManager } from './providers/app-notifications'
import { AppPopupNotificationManager } from './providers/app-popup-notifications'
import { appSettingsModalOpenProfileKey } from './providers/app-settings-modal'
import {
appSettingsModalOpenProfileKey,
appSettingsModalOpenSyncedOptionsKey,
} from './providers/app-settings-modal'
const appSettings = useAppSettings()
const appTheme = useTheme()
@@ -438,6 +448,11 @@ const os = ref('')
const isDevEnvironment = ref(false)
const stateInitialized = ref(false)
const globalSyncedOptionsQuery = useQuery({
queryKey: ['global-synced-options'],
queryFn: get_global_synced_options,
enabled: computed(() => stateInitialized.value),
})
const criticalErrorMessage = ref()
@@ -469,7 +484,24 @@ const authUnreachable = computed(() => {
return false
})
let unlistenEditMenu
function handleEditMenuAction(action) {
const event = new CustomEvent(`edit-menu:${action}`, { cancelable: true })
if (document.dispatchEvent(event)) document.execCommand(action)
}
onMounted(async () => {
try {
const listeners = await Promise.all([
listen('edit-menu://undo', () => handleEditMenuAction('undo')),
listen('edit-menu://redo', () => handleEditMenuAction('redo')),
])
unlistenEditMenu = () => listeners.forEach((unlisten) => unlisten())
} catch (error) {
handleError(error)
}
await useCheckDisableMouseover()
try {
handleAdsConsentRequired(await should_show_ads_consent_popup())
@@ -490,6 +522,7 @@ onUnmounted(async () => {
document.querySelector('body').removeEventListener('auxclick', handleAuxClick)
document.querySelector('body').removeEventListener('contextmenu', handleContextMenu)
document.removeEventListener('fullscreenchange', handleFullscreenChange)
unlistenEditMenu?.()
clearDelayedUpdatePopup()
if (fullscreenAdsWindowHold) {
@@ -557,6 +590,10 @@ const messages = defineMessages({
id: 'app.nav.modrinth-hosting',
defaultMessage: 'Modrinth Hosting',
},
screenshots: {
id: 'app.nav.screenshots',
defaultMessage: 'Screenshots',
},
createNewInstance: {
id: 'app.nav.create-new-instance',
defaultMessage: 'Create new instance',
@@ -999,6 +1036,7 @@ const updateToPlayModal = ref()
const modrinthLoginModal = ref()
const appSettingsModal = ref()
provide(appSettingsModalOpenProfileKey, () => appSettingsModal.value?.showProfile())
provide(appSettingsModalOpenSyncedOptionsKey, () => appSettingsModal.value?.showSyncedOptions())
watch(incompatibilityWarningModal, (modal) => {
if (modal) {
@@ -2025,6 +2063,13 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
<NavButton v-tooltip.right="formatMessage(appMessages.skinSelectorLabel)" to="/skins">
<ShirtIcon />
</NavButton>
<NavButton
v-if="globalSyncedOptionsQuery.data.value?.screenshots"
v-tooltip.right="formatMessage(messages.screenshots)"
to="/screenshots"
>
<ImagesIcon />
</NavButton>
<NavButton
v-tooltip.right="formatMessage(messages.modrinthHosting)"
to="/hosting/manage"