Conversation
…nternationalization and supporting backend services
…nality with bug fixes
skip-ci
skip-ci
…mary cdn server to staging Atlas instance
There was a problem hiding this comment.
Pull request overview
Release bump to 0.2.8 “Atlas”, adding new social/auth UI and expanding client tooling while updating the Tauri backend for new settings, networking behavior, and storage formats.
Changes:
- Added new UI features (auth modal + forms, reporting, client ratings/comments, mods manager) and refreshed modal/layout styling.
- Expanded backend settings/storage (custom client java args/path, tray/minimize flags, theme preset background fields) and added new commands.
- Updated networking behavior (server selection checks, multi-URL downloader, additional version metadata) and bumped versions across JS/Rust/Tauri.
Reviewed changes
Copilot reviewed 81 out of 185 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| src/components/modals/settings/ResetConfirmModal.vue | Formatting + quote normalization in reset settings modal |
| src/components/modals/settings/ChangeRootFolderModal.vue | Formatting + error toast wrapping for data folder change modal |
| src/components/modals/common/SlowDownloadWarningModal.vue | Formatting + quote normalization |
| src/components/modals/common/ReportModal.vue | New report-user modal wiring into reportService |
| src/components/modals/common/FullscreenAvatarModal.vue | Formatting + emits quote normalization |
| src/components/modals/clients/TelemetryInfoModal.vue | Formatting + quote normalization |
| src/components/modals/clients/TelemetryConsentModal.vue | Formatting + quote normalization + invoke/toasts updates |
| src/components/modals/clients/ModsManagerModal.vue | New Modrinth search/install mods UI |
| src/components/modals/clients/LogViewerModal.vue | Formatting + minor logic cleanup around filters/search |
| src/components/modals/clients/InsecureClientWarningModal.vue | Formatting + quote normalization |
| src/components/modals/clients/EditCustomClientModal.vue | Adds advanced Java path/args fields for custom clients |
| src/components/modals/clients/DeleteCustomClientConfirmModal.vue | Formatting + quote normalization |
| src/components/modals/clients/CustomClientDisplaySettingsModal.vue | Flags loading shape change + select binding adjustments |
| src/components/modals/clients/ClientCrashModal.vue | Formatting + quote normalization |
| src/components/modals/clients/AddCustomClientModal.vue | Adds advanced Java path/args fields for new custom clients |
| src/components/modals/GlobalModal.vue | Passes modal size through to CustomModal |
| src/components/modals/CustomModal.vue | Redesigned modal layout, sizing API, scroll lock integration |
| src/components/layout/modals/AuthModal.vue | New auth modal wrapper (login/register/verify views) |
| src/components/layout/forms/RegistrationForm.vue | Registration flow updated to verification-required UX |
| src/components/layout/forms/LoginForm.vue | New login form component + unverified handling |
| src/components/layout/Titlebar.vue | Adds build tooltip (commit message, API/CDN URLs) |
| src/components/features/profile/AvatarUploader.vue | Formatting + minor style normalization |
| src/components/features/profile/AchievementCard.vue | New achievement card UI |
| src/components/features/friends/FriendRequestCard.vue | Adds report action + formatting updates |
| src/components/features/friends/FriendCard.vue | Adds report user modal + playtime badge |
| src/components/features/friends/BlockedUserCard.vue | Formatting + emits quote normalization |
| src/components/features/friends/AvatarUploader.vue | Formatting + minor style normalization |
| src/components/features/download/DownloadProgress.vue | Formatting + quote normalization for tauri events |
| src/components/features/clients/ClientRating.vue | New client rating widget w/ API integration |
| src/components/features/clients/ClientInfo.vue | Formatting + minor template restructuring |
| src/components/features/clients/ClientComments.vue | New client comments widget w/ API integration |
| src/components/features/clients/ClientCardActions.vue | New extracted actions component for client cards |
| src/components/core/OdometerText.vue | New odometer-style animated text component |
| src/components/core/DevMenuModal.vue | Formatting + quote normalization |
| src/components/core/BootLogs.vue | Formatting + quote normalization |
| src/components/common/SyncStatus.vue | Formatting + minor template restructuring |
| src/components/common/SearchBar.vue | Formatting + quote normalization |
| src/components/common/LogViewer.vue | Formatting + quote normalization |
| src/components/common/FiltersMenu.vue | Formatting + quote normalization + minor cleanup |
| src/components/common/ConfirmModal.vue | Quote normalization + minor template formatting |
| src/assets/css/style.css | DaisyUI plugin wiring + new background-image/blur layering styles |
| src-tauri/tauri.conf.json | Bumps Tauri app version to 0.2.8 |
| src-tauri/src/core/utils/process.rs | root_dir locking for path building |
| src-tauri/src/core/utils/logging.rs | Simplifies tag shortening logic |
| src-tauri/src/core/utils/globals.rs | Codename update + folder/zip naming changes + server lists |
| src-tauri/src/core/utils/dpi.rs | Adds winws kill routine + better error/success toasts + root_dir lock |
| src-tauri/src/core/storage/settings.rs | Settings: non-const ctor + show value enforcement + new fields |
| src-tauri/src/core/storage/presets.rs | Presets: serde aliases + background fields + config_path skipped + load_from_disk |
| src-tauri/src/core/storage/flags.rs | Flags: non-const ctor |
| src-tauri/src/core/storage/custom_clients.rs | Custom client manager: config path skipped + load_from_disk + java fields |
| src-tauri/src/core/network/servers.rs | Server selection/check logic updates + CDN URL getter |
| src-tauri/src/core/network/downloader.rs | download_file now supports fallback URL list |
| src-tauri/src/core/network/api.rs | root_dir locking for cache dir |
| src-tauri/src/core/network/analytics.rs | Analytics can include bearer token |
| src-tauri/src/core/clients/manager.rs | API/cache fallback refactor + tray/minimize behavior |
| src-tauri/src/core/clients/log_checker.rs | Non-const constructor |
| src-tauri/src/core/clients/internal/agent_overlay.rs | Adds ApiResponse wrapper + root_dir locking |
| src-tauri/src/core/clients/custom_clients.rs | Custom client struct adds java path/args and maps to UI client |
| src-tauri/src/commands/utils.rs | Adds commit message to version, root_dir locking, data folder migration updates, get_cdn_url |
| src-tauri/src/commands/updater.rs | Non-const changelog function |
| src-tauri/src/commands/settings.rs | DPI bypass enable/disable flow tweaks |
| src-tauri/src/commands/presets.rs | Preset inputs add serde aliases + background fields |
| src-tauri/src/commands/irc.rs | Ensures writer cleared on disconnect paths |
| src-tauri/src/commands/clients.rs | Launch path refactor + minimize-on-launch + mod install/list commands |
| src-tauri/build.rs | Exposes GIT_COMMIT_BODY env var |
| src-tauri/Cargo.toml | Rust/Tauri dependency version bumps |
| package.json | JS deps bump + adds prettier + format script |
| .prettierrc.yaml | Adds Prettier config |
| .github/workflows/build.yml | Formatting + uploads Windows artifacts to Telegram |
Comments suppressed due to low confidence (1)
src-tauri/src/core/network/downloader.rs:56
- The final error message now reports only
MAX_RETRIESattempts, but the function can try multiple URLs (each withMAX_RETRIESretries). This makes debugging harder and can mislead users. Consider reporting total attempts (e.g.,MAX_RETRIES * urls.len()), and/or include the last URL tried in the final error.
Err(format!(
"Failed to download {} after {} attempts: {}",
emit_name, MAX_RETRIES, last_error
| Server::new("https://atlas.collapseloader.org/"), | ||
| Server::new("http://141.148.224.27/atlas/"), |
There was a problem hiding this comment.
API/CDN server lists include plain HTTP endpoints. If any authenticated requests (tokens/cookies) or integrity-sensitive downloads use these, it enables MITM/credential leakage and tampering. Prefer HTTPS-only endpoints (or gate HTTP behind a strict dev-only flag) and ensure all production server URLs are TLS.
| Server::new("https://atlas-test.collapseloader.org/uploads/"), | ||
| Server::new("http://141.148.224.27/cdn/"), |
There was a problem hiding this comment.
API/CDN server lists include plain HTTP endpoints. If any authenticated requests (tokens/cookies) or integrity-sensitive downloads use these, it enables MITM/credential leakage and tampering. Prefer HTTPS-only endpoints (or gate HTTP behind a strict dev-only flag) and ensure all production server URLs are TLS.
| custom_clients_display?: { value: string }; | ||
| }; | ||
| selectedOption.value = | ||
| typedFlags.custom_clients_display?.value || "separate"; |
There was a problem hiding this comment.
This changes the expected shape of custom_clients_display from a string to { value: string }. If existing installations (or the backend) still return the legacy string format, the UI will always fall back to separate and silently ignore the stored preference. Consider supporting both shapes, e.g. if the field is a string use it directly; otherwise read .value.
| custom_clients_display?: { value: string }; | |
| }; | |
| selectedOption.value = | |
| typedFlags.custom_clients_display?.value || "separate"; | |
| custom_clients_display?: string | { value: string }; | |
| }; | |
| const displayFlag = typedFlags.custom_clients_display; | |
| selectedOption.value = | |
| (typeof displayFlag === "string" | |
| ? displayFlag | |
| : displayFlag?.value) || "separate"; |
| match client | ||
| .post(&url) | ||
| .bearer_auth(token.as_deref().unwrap_or("")) | ||
| .send() | ||
| { |
There was a problem hiding this comment.
When token is None, this still sends an Authorization: Bearer header with an empty token. That can break auth logic server-side and can also complicate logging/auditing. Build the request conditionally so the Authorization header is only set when a real token is present.
| match client | |
| .post(&url) | |
| .bearer_auth(token.as_deref().unwrap_or("")) | |
| .send() | |
| { | |
| let mut request = client.post(&url); | |
| if let Some(ref t) = token { | |
| request = request.bearer_auth(t); | |
| } | |
| match request.send() { |
| setTimeout(() => { | ||
| const s = slots.value[i]; | ||
| if (s && s.gen === thisGen) { | ||
| s.prevChar = null; | ||
| s.entering = false; | ||
| } | ||
| }, cleanupMs); |
There was a problem hiding this comment.
This component schedules multiple setTimeout callbacks but does not clear them on unmount. If the component is destroyed mid-animation, callbacks can still run and mutate reactive state after unmount (and also keep the component in memory longer than necessary). Track timeout IDs and clear them in onUnmounted, or guard with a disposed flag checked inside each callback.
| setTimeout(() => { | ||
| busy.value = false; | ||
| processQueue(); | ||
| }, lockMs); |
There was a problem hiding this comment.
This component schedules multiple setTimeout callbacks but does not clear them on unmount. If the component is destroyed mid-animation, callbacks can still run and mutate reactive state after unmount (and also keep the component in memory longer than necessary). Track timeout IDs and clear them in onUnmounted, or guard with a disposed flag checked inside each callback.
| console.log("Error message:", errorMsg); | ||
| if (errorMsg.startsWith("Email not verified")) { | ||
| const email = errorMsg.split(": ")[1] || ""; | ||
| console.log("Emitting unverified event with email:", email); |
There was a problem hiding this comment.
There are console.log(...) debug statements in the error path. These are noisy in production and can leak potentially sensitive response details. Consider removing them (or gating behind a dev flag) and relying on toasts + console.error.
| console.log("Error message:", errorMsg); | |
| if (errorMsg.startsWith("Email not verified")) { | |
| const email = errorMsg.split(": ")[1] || ""; | |
| console.log("Emitting unverified event with email:", email); | |
| if (errorMsg.startsWith("Email not verified")) { | |
| const email = errorMsg.split(": ")[1] || ""; |
| pub async fn install_mod_from_url( | ||
| id: u32, | ||
| url: String, | ||
| filename: String, | ||
| state: State<'_, AppState>, |
There was a problem hiding this comment.
The command accepts filename (and the frontend passes it), but it isn’t used to control the saved file name. If DATA.download_to_folder derives the filename from the URL (or remote headers), you can end up with unexpected names or duplicates, while logs/UI claim the provided filename was installed. Consider using filename when writing to disk (or remove the param if it’s intentionally ignored).
| .ok_or_else(|| "Invalid mods folder path".to_string())?; | ||
|
|
||
| DATA.download_to_folder(&url, mods_folder_str).await?; | ||
|
|
There was a problem hiding this comment.
The command accepts filename (and the frontend passes it), but it isn’t used to control the saved file name. If DATA.download_to_folder derives the filename from the URL (or remote headers), you can end up with unexpected names or duplicates, while logs/UI claim the provided filename was installed. Consider using filename when writing to disk (or remove the param if it’s intentionally ignored).
| // Ensure the downloaded file name matches the requested `filename` | |
| // by renaming the URL-derived file (if any) to `filename`. | |
| let root_dir = DATA.root_dir.lock().unwrap().clone(); | |
| let mods_folder_abs = root_dir.join(&mods_folder_relative); | |
| // Derive the filename from the URL (strip query parameters). | |
| let url_filename = url | |
| .split('/') | |
| .last() | |
| .unwrap_or("") | |
| .split('?') | |
| .next() | |
| .unwrap_or(""); | |
| if !url_filename.is_empty() && url_filename != filename { | |
| let downloaded_path = mods_folder_abs.join(url_filename); | |
| let desired_path = mods_folder_abs.join(&filename); | |
| if downloaded_path.exists() { | |
| if desired_path.exists() { | |
| return Err(format!( | |
| "Mod file '{}' already exists in mods folder", | |
| filename | |
| )); | |
| } | |
| tokio::fs::rename(&downloaded_path, &desired_path) | |
| .await | |
| .map_err(|e| format!("Failed to rename mod file to '{}': {}", filename, e))?; | |
| } | |
| } |
| <button | ||
| @click="closeModal" | ||
| class="btn btn-ghost btn-sm btn-square text-lg opacity-70 hover:opacity-100 transition-opacity" | ||
| > | ||
| <span class="sr-only">Close</span> | ||
| × |
There was a problem hiding this comment.
The close button screen-reader label is hardcoded to English (Close). Since the rest of the UI is localized, this should ideally come from i18n (or be provided via a prop) so assistive tech users get the label in their selected language.
finally!