Skip to content

Commit 51a4840

Browse files
committed
replace avatar delete confirmation with popover
1 parent 5035959 commit 51a4840

File tree

5 files changed

+72
-50
lines changed

5 files changed

+72
-50
lines changed

apps/desktop/packages/mainWindow/src/components/ImagePicker/index.tsx

Lines changed: 59 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { createSignal, Show, mergeProps } from "solid-js"
22
import { useTransContext } from "@gd/i18n"
3-
import { Spinner } from "@gd/ui"
3+
import { Popover, PopoverContent, PopoverTrigger, Spinner } from "@gd/ui"
44

55
interface ImagePickerProps {
66
imageUrl: () => string | null
@@ -18,7 +18,7 @@ interface ImagePickerProps {
1818

1919
const ImagePicker = (props: ImagePickerProps) => {
2020
const [t] = useTransContext()
21-
const [showConfirmDelete, setShowConfirmDelete] = createSignal(false)
21+
const [isPopoverOpen, setIsPopoverOpen] = createSignal(false)
2222

2323
const merged = mergeProps(
2424
{
@@ -49,24 +49,17 @@ const ImagePicker = (props: ImagePickerProps) => {
4949
const handleDeleteClick = async (e: MouseEvent) => {
5050
e.preventDefault()
5151
e.stopPropagation()
52-
if (merged.confirmDelete) {
53-
setShowConfirmDelete(true)
54-
} else {
52+
if (!merged.confirmDelete) {
5553
await props.onDelete?.()
5654
}
55+
// If confirmDelete is true, the Popover handles showing the confirmation
5756
}
5857

5958
const handleConfirmDelete = async () => {
60-
setShowConfirmDelete(false)
59+
setIsPopoverOpen(false)
6160
await props.onDelete?.()
6261
}
6362

64-
const handleCancelDelete = (e?: MouseEvent) => {
65-
e?.preventDefault()
66-
e?.stopPropagation()
67-
setShowConfirmDelete(false)
68-
}
69-
7063
return (
7164
<div class="relative">
7265
{/* Main picker area */}
@@ -93,20 +86,6 @@ const ImagePicker = (props: ImagePickerProps) => {
9386
</div>
9487
</Show>
9588

96-
{/* Delete button - top right corner, appears only when hovering near it */}
97-
<Show
98-
when={merged.deletable && props.imageUrl() && !showConfirmDelete()}
99-
>
100-
<div class="group/delete absolute -right-3.5 -top-3.5 z-10 p-1.5">
101-
<div
102-
class="cursor-pointer rounded-full bg-darkSlate-800 p-1 opacity-0 transition-opacity group-hover/delete:opacity-100"
103-
onClick={handleDeleteClick}
104-
>
105-
<div class="i-hugeicons:delete-02 h-5 w-5 text-red-500 transition-all hover:text-red-400" />
106-
</div>
107-
</div>
108-
</Show>
109-
11089
{/* Loading overlay */}
11190
<Show when={props.isLoading?.()}>
11291
<div class="absolute inset-0 flex items-center justify-center rounded-xl bg-black/70">
@@ -115,27 +94,61 @@ const ImagePicker = (props: ImagePickerProps) => {
11594
</Show>
11695
</div>
11796

118-
{/* Confirmation dialog (inline popover style) */}
119-
<Show when={showConfirmDelete()}>
120-
<div
121-
class="absolute left-full top-0 z-50 ml-2 flex items-center gap-2 whitespace-nowrap rounded-md border border-solid border-darkSlate-600 bg-darkSlate-800 px-3 py-2 shadow-md animate-in fade-in zoom-in-95"
122-
onClick={(e) => e.stopPropagation()}
123-
>
124-
<span class="text-sm text-lightSlate-200">
125-
{t("general:_trn_delete")}?
126-
</span>
127-
<button
128-
class="rounded p-1 text-lightSlate-500 transition-colors hover:bg-darkSlate-700 hover:text-lightSlate-100"
129-
onClick={handleCancelDelete}
97+
{/* Delete button with confirmation popover */}
98+
<Show when={merged.deletable && props.imageUrl()}>
99+
<div class="absolute -right-3.5 -top-3.5 z-10">
100+
<Show
101+
when={merged.confirmDelete}
102+
fallback={
103+
<div class="group/delete p-1.5">
104+
<div
105+
class="cursor-pointer rounded-full bg-darkSlate-800 p-1 opacity-0 transition-opacity group-hover/delete:opacity-100"
106+
onClick={handleDeleteClick}
107+
>
108+
<div class="i-hugeicons:delete-02 h-5 w-5 text-red-500 transition-all hover:text-red-400" />
109+
</div>
110+
</div>
111+
}
130112
>
131-
<div class="i-hugeicons:cancel-01 h-5 w-5" />
132-
</button>
133-
<button
134-
class="rounded p-1 text-red-500 transition-colors hover:bg-red-500/20 hover:text-red-400"
135-
onClick={handleConfirmDelete}
136-
>
137-
<div class="i-hugeicons:tick-02 h-5 w-5" />
138-
</button>
113+
<Popover
114+
open={isPopoverOpen()}
115+
onOpenChange={setIsPopoverOpen}
116+
placement="right"
117+
>
118+
<PopoverTrigger
119+
as="div"
120+
class="group/delete p-1.5"
121+
onClick={(e: MouseEvent) => e.stopPropagation()}
122+
>
123+
<div class="cursor-pointer rounded-full bg-darkSlate-800 p-1 opacity-0 transition-opacity group-hover/delete:opacity-100">
124+
<div class="i-hugeicons:delete-02 h-5 w-5 text-red-500 transition-all hover:text-red-400" />
125+
</div>
126+
</PopoverTrigger>
127+
<PopoverContent
128+
class="w-auto !p-2"
129+
hideCloseButton
130+
onClick={(e: MouseEvent) => e.stopPropagation()}
131+
>
132+
<div class="flex items-center gap-2 whitespace-nowrap">
133+
<span class="text-sm text-lightSlate-200">
134+
{t("general:_trn_delete")}?
135+
</span>
136+
<button
137+
class="rounded p-1 text-lightSlate-500 transition-colors hover:bg-darkSlate-700 hover:text-lightSlate-100"
138+
onClick={() => setIsPopoverOpen(false)}
139+
>
140+
<div class="i-hugeicons:cancel-01 h-5 w-5" />
141+
</button>
142+
<button
143+
class="rounded p-1 text-red-500 transition-colors hover:bg-red-500/20 hover:text-red-400"
144+
onClick={handleConfirmDelete}
145+
>
146+
<div class="i-hugeicons:tick-02 h-5 w-5" />
147+
</button>
148+
</div>
149+
</PopoverContent>
150+
</Popover>
151+
</Show>
139152
</div>
140153
</Show>
141154
</div>

apps/desktop/packages/mainWindow/src/pages/Settings/Accounts.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ const Accounts = () => {
249249
setAvatarLoading(true)
250250
try {
251251
await uploadAvatarMutation.mutateAsync({ uuid, iconPath: filePath })
252+
toast.success(t("accounts:_trn_avatar_upload_success"))
252253
} catch (err) {
253254
console.error("Avatar upload failed:", err)
254255
toast.error(t("accounts:_trn_avatar_upload_failed"))
@@ -266,6 +267,7 @@ const Accounts = () => {
266267
try {
267268
await deleteAvatarMutation.mutateAsync(uuid)
268269
setAvatarPreview(null)
270+
toast.success(t("accounts:_trn_avatar_delete_success"))
269271
} catch (err) {
270272
console.error("Avatar deletion failed:", err)
271273
toast.error(t("accounts:_trn_avatar_delete_failed"))

crates/carbon_app/src/logger.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,14 @@ use tracing_subscriber::{
66
};
77

88
fn generate_logs_filters() -> String {
9+
#[cfg(debug_assertions)]
10+
let app_level = "carbon_app=trace";
11+
#[cfg(not(debug_assertions))]
12+
let app_level = "carbon_app=debug";
13+
914
let filters = &[
1015
"debug",
11-
"carbon_app=debug",
16+
app_level,
1217
"hyper::client::pool=warn",
1318
"reqwest::connect=warn",
1419
"hyper::proto::h1::conn=warn",

packages/i18n/locale/english/accounts.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,5 +45,7 @@
4545
"_trn_click_to_select_image": "Click to select an image",
4646
"_trn_upload": "Upload",
4747
"_trn_avatar_upload_failed": "Failed to upload avatar",
48-
"_trn_avatar_delete_failed": "Failed to delete avatar"
48+
"_trn_avatar_delete_failed": "Failed to delete avatar",
49+
"_trn_avatar_upload_success": "Avatar uploaded successfully",
50+
"_trn_avatar_delete_success": "Avatar removed successfully"
4951
}

packages/i18n/src/keys.generated.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* DO NOT EDIT THIS FILE MANUALLY
44
*
55
* Generated by: scripts/generateTranslationTypes.mjs
6-
* Generated at: 2026-01-02T23:08:24.729Z
6+
* Generated at: 2026-01-03T12:46:48.972Z
77
*
88
* This file provides type-safe translation keys for the i18n system.
99
* Use these types with the Trans component to get autocomplete and type checking.
@@ -13,7 +13,7 @@
1313

1414

1515
// Individual namespace key types
16-
export type AccountsKeys = "_trn_gdlauncher_account" | "_trn_minecraft_accounts" | "_trn_no_account_synced" | "_trn_account_expired.expiration_text" | "_trn_account_expired.expiration_description" | "_trn_account_expired.back_to_login" | "_trn_account_expired.launch_anyway" | "_trn_gdl_account.completion_form" | "_trn_gdl_account.verification" | "_trn_accounts" | "_trn_gdl_account_title" | "_trn_gdl_account_synced" | "_trn_gdl_account_not_synced" | "_trn_gdl_account_error" | "_trn_gdl_account_not_verified" | "_trn_recovery_email" | "_trn_microsoft_username" | "_trn_nickname" | "_trn_friend_code" | "_trn_microsoft_oid" | "_trn_microsoft_email" | "_trn_send_new_verification_email" | "_trn_danger_zone" | "_trn_request_account_deletion_description" | "_trn_request_account_deletion" | "_trn_remove_ms_account_with_gdl_account_removal_description" | "_trn_confirm_removal" | "_trn_cannot_request_deletion_for_time" | "_trn_link_gdl_account" | "_trn_log_out_gdl_account" | "_trn_uuid" | "_trn_active" | "_trn_status" | "_trn_username" | "_trn_type" | "_trn_actions" | "_trn_manage_accounts" | "_trn_change_recovery_email_title" | "_trn_change_recovery_email_description" | "_trn_cancel" | "_trn_confirm" | "_trn_change_avatar" | "_trn_select_avatar_image" | "_trn_click_to_select_image" | "_trn_upload" | "_trn_avatar_upload_failed" | "_trn_avatar_delete_failed";
16+
export type AccountsKeys = "_trn_gdlauncher_account" | "_trn_minecraft_accounts" | "_trn_no_account_synced" | "_trn_account_expired.expiration_text" | "_trn_account_expired.expiration_description" | "_trn_account_expired.back_to_login" | "_trn_account_expired.launch_anyway" | "_trn_gdl_account.completion_form" | "_trn_gdl_account.verification" | "_trn_accounts" | "_trn_gdl_account_title" | "_trn_gdl_account_synced" | "_trn_gdl_account_not_synced" | "_trn_gdl_account_error" | "_trn_gdl_account_not_verified" | "_trn_recovery_email" | "_trn_microsoft_username" | "_trn_nickname" | "_trn_friend_code" | "_trn_microsoft_oid" | "_trn_microsoft_email" | "_trn_send_new_verification_email" | "_trn_danger_zone" | "_trn_request_account_deletion_description" | "_trn_request_account_deletion" | "_trn_remove_ms_account_with_gdl_account_removal_description" | "_trn_confirm_removal" | "_trn_cannot_request_deletion_for_time" | "_trn_link_gdl_account" | "_trn_log_out_gdl_account" | "_trn_uuid" | "_trn_active" | "_trn_status" | "_trn_username" | "_trn_type" | "_trn_actions" | "_trn_manage_accounts" | "_trn_change_recovery_email_title" | "_trn_change_recovery_email_description" | "_trn_cancel" | "_trn_confirm" | "_trn_change_avatar" | "_trn_select_avatar_image" | "_trn_click_to_select_image" | "_trn_upload" | "_trn_avatar_upload_failed" | "_trn_avatar_delete_failed" | "_trn_avatar_upload_success" | "_trn_avatar_delete_success";
1717
export type AdsKeys = "_trn_adbanner.snapshot_title" | "_trn_adbanner.snapshot_text" | "_trn_adbanner.beta_title" | "_trn_adbanner.beta_text" | "_trn_adbanner.alpha_title" | "_trn_adbanner.alpha_text" | "_trn_why_are_ads_needed" | "_trn_paragraph-1" | "_trn_paragraph-2" | "_trn_paragraph-2-list-element-1" | "_trn_paragraph-2-list-element-2" | "_trn_paragraph-2-list-element-3" | "_trn_paragraph-3" | "_trn_paragraph-3-list-element-1" | "_trn_paragraph-3-list-element-2" | "_trn_paragraph-3-list-element-3" | "_trn_paragraph-4" | "_trn_paragraph-5" | "_trn_paragraph-6" | "_trn_paragraph-7" | "_trn_paragraph-8" | "_trn_revenue_transparency_title" | "_trn_revenue_split_gdl_title" | "_trn_revenue_split_gdl_desc" | "_trn_revenue_split_others_title" | "_trn_revenue_split_others_desc" | "_trn_got_it_thanks";
1818
export type AppKeys = "_trn_update_available_title" | "_trn_update_downloading" | "_trn_update_ready" | "_trn_update_download_button" | "_trn_update_install_button" | "_trn_update_later_button" | "_trn_update_copy_link" | "_trn_update_view_release" | "_trn_update_link_copied" | "_trn_update_version" | "_trn_update_progress" | "_trn_update_manual_required" | "_trn_update_pending_title" | "_trn_update_pending_description" | "_trn_update_up_to_date" | "_trn_update_latest_version" | "_trn_update_ready_description" | "_trn_update_installing" | "_trn_update_installing_description" | "_trn_update_failed" | "_trn_update_failed_description" | "_trn_update_checking" | "_trn_update_checking_description" | "_trn_update_timeout" | "_trn_update_timeout_description" | "_trn_update_in_progress" | "_trn_update_in_progress_description" | "_trn_update_snapshot" | "_trn_update_snapshot_description" | "_trn_update_check_failed" | "_trn_update_check_failed_description" | "_trn_update_copy_failed" | "_trn_update_copy_failed_description" | "_trn_update_pending_tooltip";
1919
export type AuthKeys = "_trn_login.titles.welcome_to_gdlauncher" | "_trn_login.titles.we_value_privacy" | "_trn_login.titles.sign_in_with_microsoft" | "_trn_login.titles.browser_authentication" | "_trn_login.titles.microsoft_code_step" | "_trn_login.titles.create_profile" | "_trn_login.titles.linked_microsoft_account" | "_trn_login.titles.create_gdl_account" | "_trn_login.titles.sync_gdl_account" | "_trn_login.titles.gdl_account_verification" | "_trn_login.titles.all_set" | "_trn_login.titles.authentication_complete" | "_trn_login.titles.authentication" | "_trn_login.titles.terms_and_privacy" | "_trn_login.titles.updated_terms" | "_trn_login.titles.something_went_wrong" | "_trn_login.sign_in" | "_trn_login.sign_in_with_microsoft_text" | "_trn_login.privacy_policy" | "_trn_login.terms_and_conditions" | "_trn_login.acceptable_use_policy" | "_trn_login.login" | "_trn_login.logout" | "_trn_login.code_expired_message" | "_trn_login.before_expiring" | "_trn_login.enter_code_in_browser" | "_trn_login.waiting_for_browser_auth" | "_trn_login.waiting_for_browser_confirmation" | "_trn_login.polling_microsoft_auth" | "_trn_login.authenticating_xbox" | "_trn_login.authenticating_minecraft" | "_trn_login.retrieving_minecraft_profile" | "_trn_login.retrieving_minecraft_entitlements" | "_trn_login.finalizing_authentication" | "_trn_login.checking_account" | "_trn_login.checking_gdl_account" | "_trn_login.open_in_browser" | "_trn_login.need_help" | "_trn_login.troubles_logging_in" | "_trn_login.link_not_working_help" | "_trn_login.refresh" | "_trn_login.read_and_accept" | "_trn_login.enable_hashed_email" | "_trn_login.agree_and_continue" | "_trn_login.next" | "_trn_login.lets_go" | "_trn_login.prev" | "_trn_login.register" | "_trn_login.link_account" | "_trn_login.request_email_change" | "_trn_login.sync_gdl_account" | "_trn_login.manage" | "_trn_login.we_value_privacy_title" | "_trn_login.renew" | "_trn_login.welcome_to" | "_trn_login.gdlauncher" | "_trn_login.we_value_privacy_text_renew" | "_trn_login.we_value_privacy_text1" | "_trn_login.we_value_privacy_text2" | "_trn_login.we_value_privacy_text3" | "_trn_login.we_value_privacy_text4" | "_trn_login.we_value_privacy_text5" | "_trn_login.manage_cmp" | "_trn_login.ad_tracking_settings_title" | "_trn_login.enter_your_recovery_email" | "_trn_login.recovery_email" | "_trn_login.recovery_email_description" | "_trn_login.enter_your_nickname" | "_trn_login.nickname" | "_trn_login.nickname_description" | "_trn_login.check_your_email_for_a_verification_link" | "_trn_login.request_a_new_verification_link" | "_trn_login.an_email_has_been_sent_to_your_email_address" | "_trn_login.email_request_wait" | "_trn_login.new_email_request_wait" | "_trn_login.verify_later" | "_trn_login.login_retry_message" | "_trn_login.welcome_back_name" | "_trn_login.gdlauncher_account_description" | "_trn_login.faqs" | "_trn_login.what_is_a_gdlauncher_account" | "_trn_login.what_is_a_gdlauncher_account_text" | "_trn_login.how_does_it_work" | "_trn_login.how_does_it_work_text" | "_trn_login.what_if_i_lose_access_to_my_microsoft_account" | "_trn_login.what_if_i_lose_access_to_my_microsoft_account_text" | "_trn_login.what_happens_if_i_skip_the_account_creation" | "_trn_login.what_happens_if_i_skip_the_account_creation_text" | "_trn_login.ready_to_launch" | "_trn_login.cloud_sync_active" | "_trn_login.cloud_account_found" | "_trn_login.sync_existing_account_description" | "_trn_login.found_existing_account_description" | "_trn_login.benefit_shared_instances" | "_trn_login.benefit_metrics_sync" | "_trn_login.benefit_settings_sync" | "_trn_login.sync_account" | "_trn_login.setup_later_in_settings" | "_trn_login.enable_cloud_sync" | "_trn_login.unlock_features_description" | "_trn_login.benefit_share_with_friends" | "_trn_login.benefit_track_metrics" | "_trn_login.benefit_sync_devices" | "_trn_login.quick_setup_time" | "_trn_login.skip_to_library" | "_trn_login.skip_for_now" | "_trn_login.continue_to_library" | "_trn_login.welcome_tagline" | "_trn_login.welcome_returning_tagline" | "_trn_login.terms_title" | "_trn_login.terms_subtitle" | "_trn_login.updated_terms_title" | "_trn_login.updated_terms_subtitle" | "_trn_login.cmp_notice_paragraph1" | "_trn_login.cmp_notice_ad_vendors_link" | "_trn_login.cmp_instructions" | "_trn_login.terms_checkbox_label" | "_trn_login.terms_link_label" | "_trn_login.privacy_link_label" | "_trn_login.expires_in" | "_trn_login.scan_qr_code" | "_trn_login.scan_to_open" | "_trn_login.step_1_open_link" | "_trn_login.step_2_enter_code" | "_trn_login.link_copied" | "_trn_login.open_microsoft_login" | "_trn_login.trouble_with_code" | "_trn_login.try_browser_instead" | "_trn_login.recommended" | "_trn_login.trouble_browser_signin" | "_trn_login.use_device_code_instead" | "_trn_login.device_code_explanation" | "_trn_login.browser_opened_toast" | "_trn_login.session_expired_title" | "_trn_login.session_expired_message" | "_trn_login.try_again" | "_trn_login.complete_signin_browser" | "_trn_login.browser_didnt_open" | "_trn_login.open_browser_manually" | "_trn_login.authenticating" | "_trn_login.still_having_trouble" | "_trn_login.try_device_code_instead" | "_trn_login.gdl_account_setup_title" | "_trn_login.unlock_cloud_features" | "_trn_login.create_account_description" | "_trn_login.benefits_label" | "_trn_login.benefit_share_instances_friends" | "_trn_login.benefit_track_metrics_playtime" | "_trn_login.benefit_sync_settings_preferences" | "_trn_login.benefit_access_anywhere" | "_trn_login.account_security_notice" | "_trn_login.enter_recovery_email_nickname" | "_trn_login.check_email_verification" | "_trn_profile_creation.title" | "_trn_profile_creation.description" | "_trn_profile_creation.invalid_format" | "_trn_profile_creation.checking" | "_trn_profile_creation.available" | "_trn_profile_creation.taken" | "_trn_profile_creation.not_allowed" | "_trn_profile_creation.requirements" | "_trn_profile_creation.create" | "_trn_profile_creation.create_profile" | "_trn_profile_creation.check_failed" | "_trn_profile_creation.created_but_failed_continue" | "_trn_profile_creation.username_invalid" | "_trn_profile_creation.username_unavailable" | "_trn_profile_creation.create_failed" | "_trn_profile_creation.username_placeholder" | "_trn_profile_creation.requirement_length" | "_trn_profile_creation.requirement_characters" | "_trn_profile_creation.requirement_no_profanity" | "_trn_profile_creation.requirement_appropriate" | "_trn_loading.taking_longer" | "_trn_auth.loading.taking_longer" | "_trn_login.welcome_title" | "_trn_login.welcome_back" | "_trn_login.password_strong" | "_trn_login.password_weak" | "_trn_login.purposes_we_use" | "_trn_login.email_required" | "_trn_login.email_invalid" | "_trn_login.nickname_required" | "_trn_login.nickname_too_short" | "_trn_login.email_verified_success" | "_trn_login.verification_email_sent" | "_trn_login.verification_sent_to" | "_trn_login.waiting_for_verification" | "_trn_login.resend_email" | "_trn_login.resend_email_cooldown" | "_trn_login.check_spam_folder" | "_trn_login.verification_email_notice" | "_trn_login.failed_to_check_account" | "_trn_login.failed_to_check_account_description";

0 commit comments

Comments
 (0)