Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
B
bit-pm
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
燕伟桐
bit-pm
Commits
2fc33d04
Unverified
提交
2fc33d04
authored
6月 17, 2025
作者:
Will Chen
提交者:
GitHub
6月 17, 2025
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Make delete app better handled & revamp error toast (#422)
Fixes #395 Fixes #270 Fixes #268
上级
df38fb0f
显示空白字符变更
内嵌
并排
正在显示
6 个修改的文件
包含
147 行增加
和
34 行删除
+147
-34
CustomErrorToast.tsx
src/components/CustomErrorToast.tsx
+80
-0
useLoadApp.ts
src/hooks/useLoadApp.ts
+3
-1
app_handlers.ts
src/ipc/handlers/app_handlers.ts
+12
-11
version_handlers.ts
src/ipc/handlers/version_handlers.ts
+2
-1
toast.tsx
src/lib/toast.tsx
+49
-21
app-details.tsx
src/pages/app-details.tsx
+1
-0
没有找到文件。
src/components/CustomErrorToast.tsx
0 → 100644
浏览文件 @
2fc33d04
import
React
from
"react"
;
import
{
toast
}
from
"sonner"
;
import
{
X
,
Copy
,
Check
}
from
"lucide-react"
;
interface
CustomErrorToastProps
{
message
:
string
;
toastId
:
string
|
number
;
copied
?:
boolean
;
onCopy
?:
()
=>
void
;
}
export
function
CustomErrorToast
({
message
,
toastId
,
copied
=
false
,
onCopy
,
}:
CustomErrorToastProps
)
{
const
handleClose
=
()
=>
{
toast
.
dismiss
(
toastId
);
};
const
handleCopy
=
()
=>
{
if
(
onCopy
)
{
onCopy
();
}
};
return
(
<
div
className=
"relative bg-red-50/95 backdrop-blur-sm border border-red-200 rounded-xl shadow-lg min-w-[400px] max-w-[500px] overflow-hidden"
>
{
/* Content */
}
<
div
className=
"p-4"
>
<
div
className=
"flex items-start"
>
<
div
className=
"flex-1"
>
<
div
className=
"flex items-center mb-3"
>
<
div
className=
"flex-shrink-0"
>
<
div
className=
"w-5 h-5 bg-gradient-to-br from-red-400 to-red-500 rounded-full flex items-center justify-center shadow-sm"
>
<
X
className=
"w-3 h-3 text-white"
/>
</
div
>
</
div
>
<
h3
className=
"ml-3 text-sm font-medium text-red-900"
>
Error
</
h3
>
{
/* Action buttons */
}
<
div
className=
"flex items-center space-x-1.5 ml-auto"
>
<
button
onClick=
{
(
e
)
=>
{
e
.
stopPropagation
();
handleCopy
();
}
}
className=
"p-1.5 text-red-500 hover:text-red-700 hover:bg-red-100/70 rounded-lg transition-all duration-150"
title=
"Copy to clipboard"
>
{
copied
?
(
<
Check
className=
"w-4 h-4 text-green-500"
/>
)
:
(
<
Copy
className=
"w-4 h-4"
/>
)
}
</
button
>
<
button
onClick=
{
(
e
)
=>
{
e
.
stopPropagation
();
handleClose
();
}
}
className=
"p-1.5 text-red-500 hover:text-red-700 hover:bg-red-100/70 rounded-lg transition-all duration-150"
title=
"Close"
>
<
X
className=
"w-4 h-4"
/>
</
button
>
</
div
>
</
div
>
<
div
>
<
p
className=
"text-sm text-red-800 leading-relaxed whitespace-pre-wrap bg-red-100/50 backdrop-blur-sm p-3 rounded-lg border border-red-200/50"
>
{
message
}
</
p
>
</
div
>
</
div
>
</
div
>
</
div
>
</
div
>
);
}
src/hooks/useLoadApp.ts
浏览文件 @
2fc33d04
...
...
@@ -23,7 +23,9 @@ export function useLoadApp(appId: number | null) {
return
ipcClient
.
getApp
(
appId
);
},
enabled
:
appId
!==
null
,
meta
:
{
showErrorToast
:
true
},
// Deliberately not showing error toast here because
// this will pop up when app is deleted.
// meta: { showErrorToast: true },
});
useEffect
(()
=>
{
...
...
src/ipc/handlers/app_handlers.ts
浏览文件 @
2fc33d04
...
...
@@ -669,26 +669,27 @@ export function registerAppHandlers() {
}
}
// Delete app files
const
appPath
=
getDyadAppPath
(
app
.
path
);
try
{
await
fsPromises
.
rm
(
appPath
,
{
recursive
:
true
,
force
:
true
});
}
catch
(
error
:
any
)
{
logger
.
error
(
`Error deleting app files for app
${
appId
}
:`
,
error
);
throw
new
Error
(
`Failed to delete app files:
${
error
.
message
}
`
);
}
// Delete app from database
try
{
await
db
.
delete
(
apps
).
where
(
eq
(
apps
.
id
,
appId
));
// Note: Associated chats will cascade delete if that's set up in the schema
return
;
// Note: Associated chats will cascade delete
}
catch
(
error
:
any
)
{
logger
.
error
(
`Error deleting app
${
appId
}
from database:`
,
error
);
throw
new
Error
(
`Failed to delete app from database:
${
error
.
message
}
`
,
);
}
// Delete app files
const
appPath
=
getDyadAppPath
(
app
.
path
);
try
{
await
fsPromises
.
rm
(
appPath
,
{
recursive
:
true
,
force
:
true
});
}
catch
(
error
:
any
)
{
logger
.
error
(
`Error deleting app files for app
${
appId
}
:`
,
error
);
throw
new
Error
(
`App deleted from database, but failed to delete app files. Please delete app files from
${
appPath
}
manually.\n\nError:
${
error
.
message
}
`
,
);
}
});
},
);
...
...
src/ipc/handlers/version_handlers.ts
浏览文件 @
2fc33d04
...
...
@@ -22,7 +22,8 @@ export function registerVersionHandlers() {
});
if
(
!
app
)
{
throw
new
Error
(
"App not found"
);
// The app might have just been deleted, so we return an empty array.
return
[];
}
const
appPath
=
getDyadAppPath
(
app
.
path
);
...
...
src/lib/toast.ts
→
src/lib/toast.ts
x
浏览文件 @
2fc33d04
import
{
toast
}
from
"sonner"
;
import
{
PostHog
}
from
"posthog-js"
;
import
React
from
"react"
;
import
{
CustomErrorToast
}
from
"../components/CustomErrorToast"
;
/**
* Toast utility functions for consistent notifications across the app
...
...
@@ -18,8 +20,54 @@ export const showSuccess = (message: string) => {
* @param message The error message to display
*/
export
const
showError
=
(
message
:
any
)
=>
{
toast
.
error
(
message
.
toString
()
);
const
errorMessage
=
message
.
toString
(
);
console
.
error
(
message
);
const
onCopy
=
(
toastId
:
string
|
number
)
=>
{
navigator
.
clipboard
.
writeText
(
errorMessage
);
// Update the toast to show the 'copied' state
toast
.
custom
(
(
t
)
=>
(
<
CustomErrorToast
message=
{
errorMessage
}
toastId=
{
t
}
copied=
{
true
}
onCopy=
{
()
=>
onCopy
(
t
)
}
/>
),
{
id
:
toastId
,
duration
:
Infinity
},
);
// After 2 seconds, revert the toast back to the original state
setTimeout
(()
=>
{
toast
.
custom
(
(
t
)
=>
(
<
CustomErrorToast
message=
{
errorMessage
}
toastId=
{
t
}
copied=
{
false
}
onCopy=
{
()
=>
onCopy
(
t
)
}
/>
),
{
id
:
toastId
,
duration
:
Infinity
},
);
},
2000
);
};
// Use custom error toast with enhanced features
const
toastId
=
toast
.
custom
(
(
t
)
=>
(
<
CustomErrorToast
message=
{
errorMessage
}
toastId=
{
t
}
onCopy=
{
()
=>
onCopy
(
t
)
}
/>
),
{
duration
:
4000
},
);
return
toastId
;
};
/**
...
...
@@ -39,26 +87,6 @@ export const showInfo = (message: string) => {
toast
.
info
(
message
);
};
/**
* Show a loading toast that can be updated with success/error
* @param loadingMessage The message to show while loading
* @param promise The promise to track
* @param successMessage Optional success message
* @param errorMessage Optional error message
*/
export
const
showLoading
=
<
T
>
(
loadingMessage
:
string
,
promise
:
Promise
<
T
>
,
successMessage
?:
string
,
errorMessage
?:
string
,
)
=>
{
return
toast
.
promise
(
promise
,
{
loading
:
loadingMessage
,
success
:
()
=>
successMessage
||
"Operation completed successfully"
,
error
:
(
err
)
=>
errorMessage
||
`Error:
${
err
.
message
||
"Unknown error"
}
`
,
});
};
export
const
showExtraFilesToast
=
({
files
,
error
,
...
...
src/pages/app-details.tsx
浏览文件 @
2fc33d04
...
...
@@ -86,6 +86,7 @@ export default function AppDetailsPage() {
await
refreshApps
();
navigate
({
to
:
"/"
,
search
:
{}
});
}
catch
(
error
)
{
setIsDeleteDialogOpen
(
false
);
showError
(
error
);
}
finally
{
setIsDeleting
(
false
);
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论