imporve oauth provider UI/UX (#2983)

* feat: imporve UI/UX

* fix: stabilize provider enabled toggle and polish custom OAuth settings UX

* fix: add access policy/message templates and persist advanced fields reliably

* fix: move template fill actions below fields and keep advanced form flow cleaner
This commit is contained in:
Seefs
2026-02-22 15:41:29 +08:00
committed by GitHub
parent 37e4fccb36
commit 29c2c895ff
12 changed files with 1375 additions and 187 deletions

View File

@@ -76,6 +76,31 @@ import {
Server,
CalendarClock,
} from 'lucide-react';
import {
SiAtlassian,
SiAuth0,
SiAuthentik,
SiBitbucket,
SiDiscord,
SiDropbox,
SiFacebook,
SiGitea,
SiGithub,
SiGitlab,
SiGoogle,
SiKeycloak,
SiLinkedin,
SiNextcloud,
SiNotion,
SiOkta,
SiOpenid,
SiReddit,
SiSlack,
SiTelegram,
SiTwitch,
SiWechat,
SiX,
} from 'react-icons/si';
// 获取侧边栏Lucide图标组件
export function getLucideIcon(key, selected = false) {
@@ -472,6 +497,106 @@ export function getLobeHubIcon(iconName, size = 14) {
return <IconComponent {...props} />;
}
const oauthProviderIconMap = {
github: SiGithub,
gitlab: SiGitlab,
gitea: SiGitea,
google: SiGoogle,
discord: SiDiscord,
facebook: SiFacebook,
linkedin: SiLinkedin,
x: SiX,
twitter: SiX,
slack: SiSlack,
telegram: SiTelegram,
wechat: SiWechat,
keycloak: SiKeycloak,
nextcloud: SiNextcloud,
authentik: SiAuthentik,
openid: SiOpenid,
okta: SiOkta,
auth0: SiAuth0,
atlassian: SiAtlassian,
bitbucket: SiBitbucket,
notion: SiNotion,
twitch: SiTwitch,
reddit: SiReddit,
dropbox: SiDropbox,
};
function isHttpUrl(value) {
return /^https?:\/\//i.test(value || '');
}
function isSimpleEmoji(value) {
if (!value) return false;
const trimmed = String(value).trim();
return trimmed.length > 0 && trimmed.length <= 4 && !isHttpUrl(trimmed);
}
function normalizeOAuthIconKey(raw) {
return raw
.trim()
.toLowerCase()
.replace(/^ri:/, '')
.replace(/^react-icons:/, '')
.replace(/^si:/, '');
}
/**
* Render custom OAuth provider icon with react-icons or URL/emoji fallback.
* Supported formats:
* - react-icons simple key: github / gitlab / google / keycloak
* - prefixed key: ri:github / si:github
* - full URL image: https://example.com/logo.png
* - emoji: 🐱
*/
export function getOAuthProviderIcon(iconName, size = 20) {
const raw = String(iconName || '').trim();
const iconSize = Number(size) > 0 ? Number(size) : 20;
if (!raw) {
return <Layers size={iconSize} color='var(--semi-color-text-2)' />;
}
if (isHttpUrl(raw)) {
return (
<img
src={raw}
alt='provider icon'
width={iconSize}
height={iconSize}
style={{ borderRadius: 4, objectFit: 'cover' }}
/>
);
}
if (isSimpleEmoji(raw)) {
return (
<span
style={{
width: iconSize,
height: iconSize,
lineHeight: `${iconSize}px`,
textAlign: 'center',
display: 'inline-block',
fontSize: Math.max(Math.floor(iconSize * 0.8), 14),
}}
>
{raw}
</span>
);
}
const key = normalizeOAuthIconKey(raw);
const IconComp = oauthProviderIconMap[key];
if (IconComp) {
return <IconComp size={iconSize} />;
}
return <Avatar size='extra-extra-small'>{raw.charAt(0).toUpperCase()}</Avatar>;
}
// 颜色列表
const colors = [
'amber',