r/reactjs•
KohpBand

Impossible to maintain loading and Extension checking state

Needs Help

Current Issue:

When GD/Imagick is installed, the "Display Settings Unavailable" warning appears briefly and then disappears

When clicking the "Refresh QR Status" button:

Button shows loading state ("Checking...")

After check completes, the warning screen pops right back even though GD/Imagick is installed

What We Need to Achieve:

Initial Page Load:

If GD/Imagick is installed: Show full display settings tab

If not installed: Show warning message with refresh button

When Refresh Button is Clicked:

Show loading state

Check GD/Imagick status on server

If newly installed: Switch to display settings view

If still not installed: Keep showing warning

Either way, loading state should end

State Persistence:

Once GD/Imagick is detected as installed, it should stay that way

No flickering between states

No reappearing warnings if extension is actually installed

WalletUpAdmin.jsx in React/Vite

```

//update the QR status check:

// QR State Management

const [qrState, setQrState] = useState({

canUseQR: window.walletUpData?.qrExtensionsAvailable === true,

isChecking: false,

gdAvailable: window.walletUpData?.gdAvailable === true,

imagickAvailable: window.walletUpData?.imagickAvailable === true

});

useEffect(() => {

console.log('QR State changed:', qrState);

}, [qrState]);

const checkQRStatus = useCallback(async () => {

try {

setQrState(prev => ({ ...prev, isChecking: true }));

console.log('Starting QR status check');

const response = await fetch(window.walletUpData.ajaxUrl, {

method: 'POST',

headers: {

'Content-Type': 'application/x-www-form-urlencoded',

},

body: new URLSearchParams({

action: 'wallet_up_check_qr_status',

nonce: window.walletUpData.nonce

})

});

console.log('Response received:', response);

const data = await response.json();

console.log('Response data:', data);

if (data.success) {

const newState = {

canUseQR: data.data.qr_extensions_available === true,

isChecking: false,

gdAvailable: data.data.gd_available,

imagickAvailable: data.data.imagick_available

};

console.log('Setting new QR state:', newState);

setQrState(newState);

// Update window data

window.walletUpData = {

...window.walletUpData,

qrExtensionsAvailable: newState.canUseQR,

gdAvailable: newState.gdAvailable,

imagickAvailable: newState.imagickAvailable

};

console.log('Updated window data:', window.walletUpData);

}

} catch (error) {

console.error('QR check error:', error);

setError('Failed to check QR status. Please try again.');

setQrState(prev => ({ ...prev, isChecking: false }));

}

}, []);

```

```

{/* Display Settings Tab */}

<TabsContent value="display">

{qrState.canUseQR ? (

<Card> <CardHeader>

<CardTitle>Display Settings</CardTitle>

<CardDescription>Customize how payment options appear</CardDescription>

</CardHeader> <CardContent> <div className="space-y-4"> <div className="space-y-2"> <div className="flex items-center space-x-2">

<Checkbox

id="hideMessage"

checked={displaySettings.hideMessage}

onCheckedChange={(checked) => handleDisplayChange('hideMessage', checked)}

disabled={displaySettings.showDefaultMessage}

/>

<Label htmlFor="hideMessage" className={displaySettings.showDefaultMessage ? "text-gray-400" : ""}>

Hide all payment messages

</Label> </div> <p className="text-sm text-gray-500">

When enabled, no messages will be shown above payment buttons or QR codes

</p> </div> <div className="space-y-2"> <div className="flex items-center space-x-2">

<Checkbox

id="showDefaultMessage"

checked={displaySettings.showDefaultMessage}

onCheckedChange={(checked) => handleDisplayChange('showDefaultMessage', checked)}

disabled={displaySettings.hideMessage}

/>

<Label htmlFor="showDefaultMessage" className={displaySettings.hideMessage ? "text-gray-400" : ""}>

Show default payment message

</Label> </div> <p className="text-sm text-gray-500">

Example: "Please Scan or Click to Pay $XX with [Payment Method]"

</p> </div> <div className="space-y-2">

<Label htmlFor="customMessage" className={displaySettings.showDefaultMessage || displaySettings.hideMessage ? "text-gray-400" : ""}>

Custom Payment Message

</Label>

<Input

id="customMessage"

placeholder="Enter your custom payment message"

value={displaySettings.customMessage || ''}

onChange={(e) => handleDisplayChange('customMessage', e.target.value)}

disabled={displaySettings.showDefaultMessage || displaySettings.hideMessage}

/>

<p className="text-sm text-gray-500">

Use $amount and $method as placeholders for dynamic values

</p> </div> <div>

<Label htmlFor="defaultMode">Default Display Mode</Label>

<Select

value={displaySettings.defaultMode}

onValueChange={(value) => handleDisplayChange('defaultMode', value)}

>

<SelectTrigger id="defaultMode"> <SelectValue placeholder="Select mode" /> </SelectTrigger> <SelectContent>

<SelectItem value="qr">QR Code</SelectItem>

<SelectItem value="button">Button</SelectItem>

<SelectItem value="link">Link</SelectItem>

</SelectContent> </Select> </div> <div>

<Label htmlFor="qrSize">QR Code Size</Label>

<Select

value={displaySettings.qrSize}

onValueChange={(value) => handleDisplayChange('qrSize', value)}

>

<SelectTrigger id="qrSize"> <SelectValue placeholder="Select size" /> </SelectTrigger> <SelectContent>

<SelectItem value="small">Small (150x150)</SelectItem>

<SelectItem value="medium">Medium (180x180)</SelectItem>

<SelectItem value="large">Large (250x250)</SelectItem>

</SelectContent> </Select> </div> <div>

<Label htmlFor="qrColor">QR Code Color</Label>

<Input

id="qrColor"

type="color"

value={displaySettings.qrColor}

onChange={(e) => handleDisplayChange('qrColor', e.target.value)}

/>

</div> <div>

<Label htmlFor="qrBackground">Background Color</Label>

<Input

id="qrBackground"

type="color"

value={displaySettings.qrBackground}

onChange={(e) => handleDisplayChange('qrBackground', e.target.value)}

/>

</div> <div className="flex items-center space-x-2">

<Checkbox

id="qrBorder"

checked={displaySettings.qrBorder}

onCheckedChange={(checked) => handleDisplayChange('qrBorder', checked)}

/>

<Label htmlFor="qrBorder">Show Border</Label>

</div> <div>

<Label htmlFor="qrStyle">QR Style</Label>

<Select

value={displaySettings.qrStyle}

onValueChange={(value) => handleDisplayChange('qrStyle', value)}

>

<SelectTrigger id="qrStyle"> <SelectValue placeholder="Select style" /> </SelectTrigger> <SelectContent>

<SelectItem value="square">Square</SelectItem>

<SelectItem value="rounded">Rounded</SelectItem>

<SelectItem value="dot">Dot</SelectItem>

</SelectContent> </Select> </div> <div className="mt-8">

<Button

onClick={saveSettings}

disabled={isSaving}

className="w-full"

>

{isSaving ? 'Saving...' : 'Save Display Settings'}

</Button>

{error && (

<Alert variant="destructive" className="mt-4">

<AlertTitle>Error</AlertTitle>

<AlertDescription>{error}</AlertDescription>

</Alert>

)}

{saveStatus === 'success' && (

<Alert className="mt-4">

<AlertTitle>Success</AlertTitle>

<AlertDescription>Display Settings saved successfully!</AlertDescription>

</Alert>

)}

</div> </div> </CardContent> </Card>

) : (

<Card> <CardHeader>

<CardTitle>Display Settings Unavailable</CardTitle>

<CardDescription>QR Code Customization requires GD or Imagick PHP extension</CardDescription>

</CardHeader> <CardContent> <Alert variant="destructive">

<AlertTitle>Extension Required</AlertTitle>

<AlertDescription>

Please contact your hosting provider to enable either GD or Imagick PHP extension to use QR code features.

</AlertDescription> </Alert> <div className="mt-4">

<Button

onClick={checkQRStatus}

disabled={qrState.isChecking}

className="w-full"

>

{qrState.isChecking ? 'Checking...' : 'Refresh QR Status'}

</Button> </div> </CardContent> </Card>

)}

</TabsContent>

```

enqueue-walletup.php

```

public function check_qr_extensions() {

if (!current_user_can('manage_options')) {

wp_send_json_error(['message' => 'Permission denied'], 403);

exit;

}

// Check extensions

$has_gd = extension_loaded('gd');

$has_imagick = extension_loaded('imagick');

$qr_available = $has_gd || $has_imagick;

// Debug logs

error_log('QR Check - GD: ' . ($has_gd ? 'Yes' : 'No'));

error_log('QR Check - Imagick: ' . ($has_imagick ? 'Yes' : 'No'));

error_log('QR Check - Available: ' . ($qr_available ? 'Yes' : 'No'));

$current_status = array(

'qr_extensions_available' => $qr_available,

'gd_available' => $has_gd,

'imagick_available' => $has_imagick

);

// Store the status

update_option('wallet_up_qr_status', $current_status);

// Send response

wp_send_json_success($current_status);

exit;

}

public function handle_qr_status_check() {

if (!current_user_can('manage_options')) {

wp_send_json_error(['message' => 'Permission denied'], 403);

exit;

}

if (!check_ajax_referer('wallet_up_nonce', 'nonce', false)) {

wp_send_json_error(['message' => 'Invalid security token'], 403);

exit;

}

$status = get_option('wallet_up_qr_status');

if (!$status) {

$status = wallet_up_check_qr_extensions();

}

wp_send_json_success($status);

}

/**

* Enqueue admin assets

*/

public function wallet_up_admin_assets($hook) {

$wallet_up_screen = get_current_screen();

if (!$wallet_up_screen || $wallet_up_screen->id !== 'toplevel_page_wallet-up') {

return;

}

if ($this->is_debug()) {

error_log('Wallet Up: Loading admin assets');

}

$manifest_path = $this->get_manifest_path();

if (!$manifest_path) {

$this->handle_build_error('Manifest file not found');

return;

}

try {

$manifest = $this->load_manifest($manifest_path);

if (!isset($manifest['src/main.jsx'])) {

throw new Exception('Main entry point not found in manifest');

}

// Get the main entry and CSS files

$main_entry = $manifest['src/main.jsx'];

$main_js = $main_entry['file'];

$main_css = isset($main_entry['css']) ? $main_entry['css'][0] : null;

$dist_path = WALLET_UP_BASE_DIR . 'walup-react/dist/';

$dist_url = WALLET_UP_BASE_URL . 'walup-react/dist/';

if ($this->is_debug()) {

$this->log_debug_info($dist_path, $main_js);

}

// Enqueue WordPress dependencies first

wp_enqueue_script('wp-element');

wp_enqueue_script('wp-i18n');

// Enqueue React application

wp_enqueue_script(

'wallet-up-react',

$dist_url . $main_js,

['wp-element', 'wp-i18n'],

$this->asset_version,

true

);

// Enqueue CSS if it exists

if ($main_css) {

wp_enqueue_style(

'wallet-up-styles',

$dist_url . $main_css,

array(),

$this->asset_version

);

}

// Get stored QR status

$qr_status = get_option('wallet_up_qr_status', array(

'gd_available' => false,

'imagick_available' => false,

'qr_available' => false

));

// If no stored status exists, check and store it

if (empty($qr_status)) {

$has_gd = extension_loaded('gd');

$has_imagick = extension_loaded('imagick');

$qr_available = $has_gd || $has_imagick;

$qr_status = array(

'gd_available' => $has_gd,

'imagick_available' => $has_imagick,

'qr_available' => $qr_available

);

update_option('wallet_up_qr_status', $qr_status);

}

// Single localization of walletUpData

wp_localize_script(

'wallet-up-react',

'walletUpData',

array(

'debug' => $this->is_debug(),

'nonce' => wp_create_nonce('wallet_up_nonce'),

'ajaxUrl' => admin_url('admin-ajax.php'),

'settings' => $this->get_sanitized_settings(),

'displaySettings' => $this->get_display_settings(),

'paths' => [

'assets' => esc_url_raw(WALLET_UP_BASE_URL . 'assets/'),

],

'i18n' => $this->get_i18n_strings(),

'qrExtensionsAvailable' => $qr_status['qr_available'],

'gdAvailable' => $qr_status['gd_available'],

'imagickAvailable' => $qr_status['imagick_available']

)

);

// Separate localization for AJAX data

wp_localize_script(

'wallet-up-react',

'walletUpAjax',

array(

'ajax_url' => admin_url('admin-ajax.php'),

'nonce' => wp_create_nonce('wallet_up_nonce')

)

);

} catch (Exception $e) {

if ($this->is_debug()) {

error_log('Wallet Up: Asset loading error - ' . $e->getMessage());

}

$this->handle_build_error($e->getMessage());

}

}

```

wallet-up.php (main plugin)

```

function wallet_up_check_qr_extensions() {

// Check extensions

$has_gd = extension_loaded('gd');

$has_imagick = extension_loaded('imagick');

$qr_available = $has_gd || $has_imagick;

// Store status

$current_status = array(

'qr_extensions_available' => $qr_available,

'gd_available' => $has_gd,

'imagick_available' => $has_imagick,

'qr_available' => $qr_available

);

update_option('wallet_up_qr_status', $current_status);

if (defined('WP_DEBUG') && WP_DEBUG) {

error_log('WalletUp: QR status - GD: ' . ($has_gd ? 'Yes' : 'No') .

', Imagick: ' . ($has_imagick ? 'Yes' : 'No'));

}

return $current_status;

}

// Register activation and initialization hooks

register_activation_hook(__FILE__, function() {

wallet_up_check_qr_extensions();

wallet_up_install_analytics();

});

// Run check on key WordPress events

add_action('plugins_loaded', 'wallet_up_check_qr_extensions', 10);

add_action('admin_init', 'wallet_up_check_qr_extensions', 10);

function wallet_up_admin_notice() {

echo '<div class="notice notice-error"><p>' . esc_html__('Wallet Up QR Code generation requires either GD or Imagick PHP extension. Please contact your hosting provider to enable one of these extensions.', 'walletup') . '</p></div>';

}

// Include necessary WordPress core files

include_once(ABSPATH . 'wp-admin/includes/plugin.php');

include_once(ABSPATH . 'wp-includes/pluggable.php');

include_once(ABSPATH . 'wp-includes/option.php');

// Add filters for shortcode support in widgets

add_filter('widget_text', 'shortcode_unautop');

add_filter('widget_text', 'do_shortcode');

// Register activation hook

register_activation_hook(__FILE__, 'wallet_up_install_analytics');

```

Posted Jan 4, 2025, 9:08 PM UTC

11
Loading comments…