Add site info and avatar display options to repo card

Introduces options to show site information and owner avatar in the repository card, including avatar size selection. Updates PHP, JS, and CSS to support new UI elements, avatar caching, and cache clearing via AJAX. Enhances visual presentation and user customization for embedded repository cards.
This commit is contained in:
feibisi 2025-07-31 14:45:54 +08:00
parent 105d769c7f
commit 0c4746d0d3
3 changed files with 392 additions and 18 deletions

View file

@ -67,6 +67,18 @@
type: 'boolean',
default: true
},
showAvatar: {
type: 'boolean',
default: true
},
showSiteInfo: {
type: 'boolean',
default: true
},
avatarSize: {
type: 'string',
default: 'medium'
},
cardStyle: {
type: 'string',
default: 'default'
@ -86,7 +98,7 @@
const {
platform, owner, repo, showDescription, showStats, showLanguage,
showActions, showViewButton, showCloneButton, showDownloadButton,
cardStyle, buttonStyle, alignment
showAvatar, showSiteInfo, avatarSize, cardStyle, buttonStyle, alignment
} = attributes;
const [repoData, setRepoData] = useState(null);
@ -167,24 +179,59 @@
}
const cardClass = `git-embed-card${cardStyle !== 'default' ? ` git-embed-card-${cardStyle}` : ''}`;
const avatarClass = `git-embed-avatar git-embed-avatar-${avatarSize}`;
const downloadUrl = repoData.archive_url ?
repoData.archive_url.replace('{archive_format}', 'zipball').replace('{/ref}', '/main') : '';
return el('div', { className: cardClass },
el('div', { className: 'git-embed-header' },
el('h3', { className: 'git-embed-title' },
el('span', { className: 'dashicons dashicons-admin-links git-embed-repo-icon' }),
showSiteInfo && repoData.site_info && el('div', { className: 'git-embed-site-info' },
el('img', {
src: repoData.site_info.favicon,
alt: repoData.site_info.name,
className: 'git-embed-site-favicon'
}),
el('span', { className: 'git-embed-site-name' },
el('a', {
href: repoData.html_url,
href: repoData.site_info.url,
target: '_blank',
rel: 'noopener'
}, repoData.full_name)
}, repoData.site_info.name)
)
),
el('div', { className: 'git-embed-header' },
el('div', { className: 'git-embed-title-section' },
showAvatar && repoData.owner && el('img', {
src: repoData.owner.avatar_url,
alt: repoData.owner.login,
className: avatarClass
}),
el('div', { className: 'git-embed-title-content' },
el('h3', { className: 'git-embed-title' },
el('span', { className: 'dashicons dashicons-admin-links git-embed-repo-icon' }),
el('a', {
href: repoData.html_url,
target: '_blank',
rel: 'noopener'
}, repoData.full_name)
),
showAvatar && repoData.owner && el('div', { className: 'git-embed-owner-info' },
el('span', { className: 'git-embed-owner-type' }, repoData.owner.type),
el('a', {
href: repoData.owner.html_url,
target: '_blank',
rel: 'noopener',
className: 'git-embed-owner-link'
}, `@${repoData.owner.login}`)
)
)
),
showLanguage && repoData.language && el('span', { className: 'git-embed-language' },
el('span', { className: 'dashicons dashicons-editor-code' }),
repoData.language
)
),
showDescription && repoData.description &&
el('p', { className: 'git-embed-description' },
el('span', { className: 'dashicons dashicons-text-page' }),
@ -279,6 +326,26 @@
title: __('Display Options', 'git-embed-feicode'),
initialOpen: false
},
el(ToggleControl, {
label: __('Show Site Information', 'git-embed-feicode'),
checked: showSiteInfo,
onChange: (value) => setAttributes({ showSiteInfo: value })
}),
el(ToggleControl, {
label: __('Show Owner Avatar', 'git-embed-feicode'),
checked: showAvatar,
onChange: (value) => setAttributes({ showAvatar: value })
}),
showAvatar && el(SelectControl, {
label: __('Avatar Size', 'git-embed-feicode'),
value: avatarSize,
options: [
{ label: 'Small', value: 'small' },
{ label: 'Medium', value: 'medium' },
{ label: 'Large', value: 'large' }
],
onChange: (value) => setAttributes({ avatarSize: value })
}),
el(ToggleControl, {
label: __('Show Description', 'git-embed-feicode'),
checked: showDescription,

View file

@ -23,6 +23,8 @@ class GitEmbedFeiCode {
add_action('init', [$this, 'init']);
add_action('wp_ajax_git_embed_fetch', [$this, 'ajax_fetch_repo']);
add_action('wp_ajax_nopriv_git_embed_fetch', [$this, 'ajax_fetch_repo']);
add_action('wp_ajax_git_embed_clear_cache', [$this, 'ajax_clear_cache']);
register_deactivation_hook(__FILE__, [$this, 'clear_all_cache']);
}
public function init(): void {
@ -85,6 +87,18 @@ class GitEmbedFeiCode {
'type' => 'string',
'default' => 'primary'
],
'showAvatar' => [
'type' => 'boolean',
'default' => true
],
'showSiteInfo' => [
'type' => 'boolean',
'default' => true
],
'avatarSize' => [
'type' => 'string',
'default' => 'medium'
],
'alignment' => [
'type' => 'string',
'default' => 'none'
@ -108,7 +122,8 @@ class GitEmbedFeiCode {
wp_localize_script('git-embed-feicode-editor', 'gitEmbedAjax', [
'url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('git_embed_nonce')
'nonce' => wp_create_nonce('git_embed_nonce'),
'cache_nonce' => wp_create_nonce('git_embed_cache_nonce')
]);
}
@ -144,9 +159,10 @@ class GitEmbedFeiCode {
$url = "https://api.github.com/repos/{$owner}/{$repo}";
$response = wp_remote_get($url, [
'timeout' => 10,
'timeout' => 15,
'headers' => [
'User-Agent' => 'Git-Embed-FeiCode/1.0'
'User-Agent' => 'Git-Embed-FeiCode/1.0',
'Accept' => 'application/vnd.github.v3+json'
]
]);
@ -170,14 +186,51 @@ class GitEmbedFeiCode {
'forks_count' => $data['forks_count'],
'open_issues_count' => $data['open_issues_count'],
'clone_url' => $data['clone_url'],
'archive_url' => $data['archive_url']
'archive_url' => $data['archive_url'],
'owner' => [
'login' => $data['owner']['login'],
'avatar_url' => $data['owner']['avatar_url'],
'html_url' => $data['owner']['html_url'],
'type' => $data['owner']['type']
],
'site_info' => [
'name' => 'GitHub',
'url' => 'https://github.com',
'favicon' => 'https://github.com/favicon.ico',
'color' => '#24292f'
]
];
set_transient($cache_key, $repo_data, HOUR_IN_SECONDS);
set_transient($cache_key, $repo_data, DAY_IN_SECONDS);
$this->cache_avatar($repo_data['owner']['avatar_url']);
return $repo_data;
}
private function cache_avatar(string $avatar_url): void {
if (empty($avatar_url)) {
return;
}
$cache_key = 'git_embed_avatar_' . md5($avatar_url);
if (get_transient($cache_key) !== false) {
return;
}
$response = wp_remote_get($avatar_url, [
'timeout' => 10,
'headers' => [
'User-Agent' => 'Git-Embed-FeiCode/1.0'
]
]);
if (!is_wp_error($response) && wp_remote_retrieve_response_code($response) === 200) {
set_transient($cache_key, true, WEEK_IN_SECONDS);
}
}
private function render_repository_card(array $repo_data, array $attributes): string {
$show_description = $attributes['showDescription'] ?? true;
$show_stats = $attributes['showStats'] ?? true;
@ -186,12 +239,16 @@ class GitEmbedFeiCode {
$show_view_button = $attributes['showViewButton'] ?? true;
$show_clone_button = $attributes['showCloneButton'] ?? true;
$show_download_button = $attributes['showDownloadButton'] ?? true;
$show_avatar = $attributes['showAvatar'] ?? true;
$show_site_info = $attributes['showSiteInfo'] ?? true;
$avatar_size = $attributes['avatarSize'] ?? 'medium';
$card_style = $attributes['cardStyle'] ?? 'default';
$button_style = $attributes['buttonStyle'] ?? 'primary';
$alignment = $attributes['alignment'] ?? 'none';
$align_class = $alignment !== 'none' ? " align{$alignment}" : '';
$card_class = $card_style !== 'default' ? " git-embed-card-{$card_style}" : '';
$avatar_class = "git-embed-avatar-{$avatar_size}";
$download_url = str_replace('{archive_format}', 'zipball', $repo_data['archive_url']);
$download_url = str_replace('{/ref}', '/main', $download_url);
@ -200,13 +257,50 @@ class GitEmbedFeiCode {
?>
<div class="wp-block-git-embed-feicode-repository<?php echo esc_attr($align_class); ?>">
<div class="git-embed-card<?php echo esc_attr($card_class); ?>">
<?php if ($show_site_info): ?>
<div class="git-embed-site-info">
<img src="<?php echo esc_url($repo_data['site_info']['favicon']); ?>"
alt="<?php echo esc_attr($repo_data['site_info']['name']); ?>"
class="git-embed-site-favicon"
loading="lazy">
<span class="git-embed-site-name">
<a href="<?php echo esc_url($repo_data['site_info']['url']); ?>"
target="_blank" rel="noopener">
<?php echo esc_html($repo_data['site_info']['name']); ?>
</a>
</span>
</div>
<?php endif; ?>
<div class="git-embed-header">
<h3 class="git-embed-title">
<span class="dashicons dashicons-admin-links git-embed-repo-icon"></span>
<a href="<?php echo esc_url($repo_data['html_url']); ?>" target="_blank" rel="noopener">
<?php echo esc_html($repo_data['full_name']); ?>
</a>
</h3>
<div class="git-embed-title-section">
<?php if ($show_avatar): ?>
<img src="<?php echo esc_url($repo_data['owner']['avatar_url']); ?>"
alt="<?php echo esc_attr($repo_data['owner']['login']); ?>"
class="git-embed-avatar <?php echo esc_attr($avatar_class); ?>"
loading="lazy">
<?php endif; ?>
<div class="git-embed-title-content">
<h3 class="git-embed-title">
<span class="dashicons dashicons-admin-links git-embed-repo-icon"></span>
<a href="<?php echo esc_url($repo_data['html_url']); ?>" target="_blank" rel="noopener">
<?php echo esc_html($repo_data['full_name']); ?>
</a>
</h3>
<?php if ($show_avatar): ?>
<div class="git-embed-owner-info">
<span class="git-embed-owner-type"><?php echo esc_html($repo_data['owner']['type']); ?></span>
<a href="<?php echo esc_url($repo_data['owner']['html_url']); ?>"
target="_blank" rel="noopener" class="git-embed-owner-link">
@<?php echo esc_html($repo_data['owner']['login']); ?>
</a>
</div>
<?php endif; ?>
</div>
</div>
<?php if ($show_language && $repo_data['language']): ?>
<span class="git-embed-language">
<span class="dashicons dashicons-editor-code"></span>
@ -334,6 +428,61 @@ class GitEmbedFeiCode {
wp_send_json_success($repo_data);
}
public function ajax_clear_cache(): void {
check_ajax_referer('git_embed_cache_nonce', 'nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error('Insufficient permissions');
}
$platform = sanitize_text_field($_POST['platform'] ?? '');
$owner = sanitize_text_field($_POST['owner'] ?? '');
$repo = sanitize_text_field($_POST['repo'] ?? '');
if (empty($owner) || empty($repo)) {
wp_send_json_error('Repository information required');
}
$this->clear_repository_cache($platform, $owner, $repo);
wp_send_json_success('Cache cleared successfully');
}
private function clear_repository_cache(string $platform, string $owner, string $repo): void {
$cache_key = "git_embed_{$platform}_{$owner}_{$repo}";
delete_transient($cache_key);
global $wpdb;
$wpdb->query(
$wpdb->prepare(
"DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
$wpdb->esc_like('_transient_git_embed_avatar_') . '%'
)
);
$wpdb->query(
$wpdb->prepare(
"DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
$wpdb->esc_like('_transient_timeout_git_embed_avatar_') . '%'
)
);
}
public function clear_all_cache(): void {
global $wpdb;
$wpdb->query(
$wpdb->prepare(
"DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
$wpdb->esc_like('_transient_git_embed_') . '%'
)
);
$wpdb->query(
$wpdb->prepare(
"DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
$wpdb->esc_like('_transient_timeout_git_embed_') . '%'
)
);
}
private function load_textdomain(): void {
load_plugin_textdomain(
'git-embed-feicode',

160
style.css
View file

@ -58,13 +58,108 @@
border: none;
}
.git-embed-header {
.git-embed-site-info {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 1px solid #e1e4e8;
}
.git-embed-site-favicon {
width: 16px;
height: 16px;
flex-shrink: 0;
}
.git-embed-site-name {
font-size: 12px;
color: #656d76;
font-weight: 500;
}
.git-embed-site-name a {
color: #656d76;
text-decoration: none;
}
.git-embed-site-name a:hover {
color: #0969da;
text-decoration: underline;
}
.git-embed-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
margin-bottom: 12px;
flex-wrap: wrap;
gap: 12px;
}
.git-embed-title-section {
display: flex;
align-items: flex-start;
gap: 12px;
flex: 1;
min-width: 0;
}
.git-embed-title-content {
flex: 1;
min-width: 0;
}
.git-embed-avatar {
border-radius: 50%;
flex-shrink: 0;
border: 2px solid #e1e4e8;
}
.git-embed-avatar-small {
width: 32px;
height: 32px;
}
.git-embed-avatar-medium {
width: 40px;
height: 40px;
}
.git-embed-avatar-large {
width: 48px;
height: 48px;
}
.git-embed-owner-info {
display: flex;
align-items: center;
gap: 8px;
margin-top: 4px;
font-size: 12px;
}
.git-embed-owner-type {
background: #f1f8ff;
color: #0969da;
padding: 2px 6px;
border-radius: 12px;
font-size: 10px;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.git-embed-owner-link {
color: #656d76;
text-decoration: none;
font-weight: 500;
}
.git-embed-owner-link:hover {
color: #0969da;
text-decoration: underline;
}
.git-embed-title {
@ -299,9 +394,35 @@
padding: 16px;
}
.git-embed-site-info {
margin-bottom: 12px;
padding-bottom: 8px;
}
.git-embed-title-section {
gap: 8px;
}
.git-embed-avatar-large {
width: 40px;
height: 40px;
}
.git-embed-avatar-medium {
width: 32px;
height: 32px;
}
.git-embed-owner-info {
flex-direction: column;
align-items: flex-start;
gap: 4px;
}
.git-embed-header {
flex-direction: column;
align-items: flex-start;
gap: 8px;
}
.git-embed-stats {
@ -332,6 +453,19 @@
font-size: 16px;
}
.git-embed-title-section {
flex-direction: column;
gap: 8px;
}
.git-embed-avatar {
align-self: flex-start;
}
.git-embed-owner-info {
margin-top: 0;
}
.git-embed-actions {
flex-direction: column;
}
@ -345,6 +479,10 @@
text-align: center;
gap: 2px;
}
.git-embed-site-info {
justify-content: center;
}
}
.wp-block[data-type="git-embed-feicode/repository"] {
@ -365,4 +503,24 @@
.components-panel__body .components-select-control:disabled {
opacity: 0.3;
}
.git-embed-card img {
max-width: 100%;
height: auto;
}
.git-embed-site-favicon,
.git-embed-avatar {
image-rendering: -webkit-optimize-contrast;
image-rendering: crisp-edges;
}
.git-embed-card .git-embed-avatar:hover {
transform: scale(1.05);
transition: transform 0.2s ease-in-out;
}
.git-embed-owner-type:empty {
display: none;
}