settings = $settings ?? new Settings(); } /** * Read one global widget value. * * @param string $key Field key, e.g. 'botName'. * @param mixed $fallback Value to use when nothing is saved. Null means * "use the schema default". * @return mixed */ public function widget( string $key, $fallback = null ) { if ( null === $fallback ) { $schema = Panel_Registry::get_widget_schema(); if ( array_key_exists( $key, $schema ) ) { $fallback = $schema[ $key ]['default']; } } return $this->settings->get( 'publicChat.widget.' . $key, $fallback ); } /** * Read one value belonging to a single integration. * * @param string $id Panel id, e.g. 'woocommerce'. * @param string $key Field key, e.g. 'recommendedLimit'. * @param mixed $fallback Value to use when nothing is saved. Null means * "use the panel's declared default". * @return mixed */ public function integration( string $id, string $key, $fallback = null ) { if ( null === $fallback ) { $panel = Panel_Registry::get_panel( $id ); if ( null !== $panel && array_key_exists( $key, $panel['fields'] ) ) { $fallback = $panel['fields'][ $key ]['default']; } } return $this->settings->get( 'publicChat.integrations.' . $id . '.' . $key, $fallback ); } /** * Whether one integration is switched on and its plugin is active. * * @param string $id Panel id. * @return bool */ public function is_enabled( string $id ): bool { $panel = Panel_Registry::get_panel( $id ); if ( null === $panel || ! call_user_func( $panel['available'] ) ) { return false; } return (bool) $this->integration( $id, 'enabled', false ); } /** * Whether at least one integration is usable right now. * * This is the gate for loading the storefront widget and for accepting a * public chat request. * * @return bool */ public function any_enabled(): bool { foreach ( array_keys( Panel_Registry::get_active_panels() ) as $id ) { if ( $this->is_enabled( (string) $id ) ) { return true; } } return false; } /** * Storefront topic cards: one per enabled panel that declares a topic. * * @return array List of [ id, label, description, icon, prompts ] (prompts: that panel's first 3 starter prompts). */ public function topics(): array { $topics = []; foreach ( $this->panels_with_topics() as $id => $topic ) { $prompts = $this->integration( $id, 'starterPrompts' ); $prompts = is_array( $prompts ) ? $prompts : []; $topics[] = [ 'id' => $id, 'label' => $topic['label'], 'description' => $topic['description'], 'icon' => $topic['icon'], // Unique: each prompt is one chip, keyed on its text. 'prompts' => array_slice( array_values( array_unique( array_filter( $prompts, static fn( $prompt ) => is_string( $prompt ) && '' !== trim( $prompt ) ) ) ), 0, 3 ), ]; } return $topics; } /** * The topic the current page belongs to, e.g. 'booktics' on a service page. * * @return string Panel id, or '' when no enabled panel claims the page. */ public function page_topic(): string { foreach ( $this->panels_with_topics() as $id => $topic ) { if ( null === $topic['matches_page'] ) { continue; } try { if ( call_user_func( $topic['matches_page'] ) ) { return $id; } } catch ( \Throwable $e ) { // A plugin's broken check must not break the widget: treat it as no match. continue; } } return ''; } /** * Enabled, available panels that declare a topic. * * @return array Panel id => normalized topic. */ private function panels_with_topics(): array { $out = []; foreach ( Panel_Registry::get_active_panels() as $id => $panel ) { $id = (string) $id; if ( null !== ( $panel['topic'] ?? null ) && $this->is_enabled( $id ) ) { $out[ $id ] = $panel['topic']; } } return $out; } /** * Starter prompts from every enabled integration, joined into one list. * * Integrations take turns (first prompt of each, then the second of each…) * so a third plugin is not crowded out by the first two. Duplicates are * dropped so two integrations offering the same wording do not show the * same chip twice. * * @param int $limit Maximum number of prompts to return. * @return array */ public function starter_prompts( int $limit = 6 ): array { $queues = []; foreach ( array_keys( Panel_Registry::get_active_panels() ) as $id ) { $id = (string) $id; if ( ! $this->is_enabled( $id ) ) { continue; } // No explicit fallback: an unsaved list must fall through to the // panel's declared default, which an explicit [] would suppress. $prompts = $this->integration( $id, 'starterPrompts' ); if ( ! is_array( $prompts ) ) { continue; } $queues[] = array_values( array_filter( $prompts, static fn( $prompt ) => is_string( $prompt ) && '' !== trim( $prompt ) ) ); } $list = []; for ( $round = 0; $queues; $round++ ) { foreach ( $queues as $i => $queue ) { if ( ! isset( $queue[ $round ] ) ) { unset( $queues[ $i ] ); continue; } if ( in_array( $queue[ $round ], $list, true ) ) { continue; } $list[] = $queue[ $round ]; if ( count( $list ) >= $limit ) { return $list; } } } return $list; } }