Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 85 additions & 1 deletion src/css/settings.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@use 'common/codemirror';

$sections: general, editor, debug;
$sections: general, editor, debug, version-switch;

p.submit {
display: flex;
Expand Down Expand Up @@ -127,3 +127,87 @@ body.js {
.cloud-settings tbody tr:nth-child(n+5) {
display: none;
}

// Version Switch Styles
.code-snippets-version-switch {
.current-version {
font-family: monospace;
font-size: 1.1em;
font-weight: bold;
color: #0073aa;
background: #f0f6fc;
padding: 2px 8px;
border-radius: 3px;
border: 1px solid #c3c4c7;
}

#target_version {
min-width: 200px;
margin-inline-start: 8px;
}

#switch-version-btn {
&[disabled] {
opacity: 0.6;
cursor: not-allowed;
background-color: #f0f0f1 !important;
color: #a7aaad !important;
border-color: #dcdcde !important;
}
}

// Warning box styling
#version-switch-warning {
margin-top: 20px !important;
padding: 12px 16px;
border-left: 4px solid #dba617;
background: #fff8e5;
border-radius: 4px;

p {
margin: 0;
color: #8f6914;

strong {
color: #8f6914;
}
}
}

#version-switch-result {
margin-block-start: 12px;

&.notice {
padding: 8px 12px;
border-radius: 4px;
}
}

.notice {
&.notice {
&-success {
border-left-color: #00a32a;
}

&-error {
border-left-color: #d63638;
}

&-warning {
border-left-color: #dba617;
}

&-info {
border-left-color: #72aee6;
}
}
}
}

.version-switch-settings {
.form-table {
th {
width: 180px;
}
}
}
1 change: 1 addition & 0 deletions src/js/services/settings/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { handleSettingsTabs } from './tabs'
export { handleEditorPreviewUpdates } from './editor-preview'
export { initVersionSwitch } from './version'
182 changes: 182 additions & 0 deletions src/js/services/settings/version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
// Handles version switching UI on the settings screen.
// Exported init function so callers can opt-in like other settings modules.
// Uses vanilla DOM APIs and the global `code_snippets_version_switch` config
// injected by PHP via wp_add_inline_script.

interface VersionConfig {
ajaxurl?: string
nonce_switch?: string
nonce_refresh?: string

}

interface AjaxResponse {
success?: boolean
data?: {
message?: string
}
}

declare global {
interface Window {
code_snippets_version_switch?: VersionConfig
__code_snippets_i18n?: Record<string, string>
}
}

const el = (id: string): HTMLElement | null => document.getElementById(id)

const getConfig = (): VersionConfig => {
const w = <{ code_snippets_version_switch?: VersionConfig }><unknown>window
return w.code_snippets_version_switch ?? {}
}

const getCurrentVersion = (): string => (document.querySelector('.current-version')?.textContent ?? '').trim()

const getI18n = (key: string, fallback: string): string => window.__code_snippets_i18n?.[key] ?? fallback

const bindDropdown = (
dropdown: HTMLSelectElement,
button: HTMLButtonElement | null,
currentVersion: string,
): void => {
dropdown.addEventListener('change', (): void => {
const selectedVersion = dropdown.value
if (!button) {
return
}
if (!selectedVersion || selectedVersion === currentVersion) {
button.disabled = true
const warn = el('version-switch-warning')
if (warn) { warn.setAttribute('style', 'display: none;') }
} else {
button.disabled = false
const warn = el('version-switch-warning')
if (warn) { warn.setAttribute('style', '') }
}
})
}

const SUCCESS_RELOAD_MS = 3000

const postForm = async (data: Record<string, string>, cfg: VersionConfig): Promise<AjaxResponse> => {
const body = new URLSearchParams()
Object.keys(data).forEach(k => body.append(k, data[k]))
const resp = await fetch(cfg.ajaxurl ?? '/wp-admin/admin-ajax.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
body: body.toString(),
credentials: 'same-origin',
})
const json = <AjaxResponse> await resp.json()
return json
}

const bindSwitch = (
button: HTMLButtonElement,
dropdown: HTMLSelectElement,
result: HTMLDivElement,
cfg: VersionConfig,
currentVersion: string,
): void => {
button.addEventListener('click', (): void => {
void (async (): Promise<void> => {
const targetVersion = dropdown.value
if (!targetVersion || targetVersion === currentVersion) {
result.className = 'notice notice-warning'
result.innerHTML = `<p>${getI18n('selectDifferent', 'Please select a different version to switch to.')}</p>`
result.style.display = ''
return
}

button.disabled = true
const originalText = button.textContent ?? ''
button.textContent = getI18n('switching', 'Switching...')

result.className = 'notice notice-info'
result.innerHTML = `<p>${getI18n('processing', 'Processing version switch. Please wait...')}</p>`
result.style.display = ''

try {
const response = await postForm({
action: 'code_snippets_switch_version',
target_version: targetVersion,
nonce: cfg.nonce_switch ?? '',
}, cfg)

if (response.success) {
result.className = 'notice notice-success'
result.innerHTML = `<p>${response.data?.message ?? ''}</p>`
setTimeout(() => window.location.reload(), SUCCESS_RELOAD_MS)
return
}

result.className = 'notice notice-error'
result.innerHTML = `<p>${response.data?.message ?? getI18n('error', 'An error occurred.')}</p>`
button.disabled = false
button.textContent = originalText
} catch (_err) {
result.className = 'notice notice-error'
result.innerHTML = `<p>${getI18n('errorSwitch', 'An error occurred while switching versions. Please try again.')}</p>`
button.disabled = false
button.textContent = originalText
}
})()
})
}

const REFRESH_RELOAD_MS = 1000

const bindRefresh = (
btn: HTMLButtonElement,
cfg: VersionConfig,
): void => {
btn.addEventListener('click', (): void => {
void (async (): Promise<void> => {
const original = btn.textContent ?? ''
btn.disabled = true
btn.textContent = getI18n('refreshing', 'Refreshing...')

try {
await postForm({
action: 'code_snippets_refresh_versions',
nonce: cfg.nonce_refresh ?? '',
}, cfg)

btn.textContent = getI18n('refreshed', 'Refreshed!')
setTimeout(() => {
btn.disabled = false
btn.textContent = original
window.location.reload()
}, REFRESH_RELOAD_MS)
} catch {
btn.disabled = false
btn.textContent = original
}
})()
})
}

export const initVersionSwitch = (): void => {
const cfg = getConfig()
const currentVersion = getCurrentVersion()

const button = <HTMLButtonElement | null> el('switch-version-btn')
const dropdown = <HTMLSelectElement | null> el('target_version')
const result = <HTMLDivElement | null> el('version-switch-result')
const refreshBtn = <HTMLButtonElement | null> el('refresh-versions-btn')

if (dropdown) {
bindDropdown(dropdown, button, currentVersion)
}

if (button && dropdown && result) {
bindSwitch(button, dropdown, result, cfg, currentVersion)
}

if (refreshBtn) {
bindRefresh(refreshBtn, cfg)
}
}


3 changes: 2 additions & 1 deletion src/js/settings.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { handleEditorPreviewUpdates, handleSettingsTabs } from './services/settings'
import { handleEditorPreviewUpdates, handleSettingsTabs, initVersionSwitch } from './services/settings'

handleSettingsTabs()
handleEditorPreviewUpdates()
initVersionSwitch()
1 change: 1 addition & 0 deletions src/php/class-plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ public function load_plugin() {
// Settings component.
require_once $includes_path . '/settings/settings-fields.php';
require_once $includes_path . '/settings/editor-preview.php';
require_once $includes_path . '/settings/class-version-switch.php';
require_once $includes_path . '/settings/settings.php';

// Cloud List Table shared functions.
Expand Down
6 changes: 5 additions & 1 deletion src/php/settings/class-setting-field.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,11 @@ public function render() {
* Render a callback field.
*/
public function render_callback_field() {
call_user_func( $this->render_callback );
if ( ! is_callable( $this->render_callback ) ) {
return;
}

call_user_func( $this->render_callback, $this->args );
}

/**
Expand Down
Loading
Loading