|\WP_Error|null */ private static $primed = null; /** * Register hooks. * * @return void */ public static function register(): void { add_action( 'admin_post_' . self::TEST_ACTION, array( self::class, 'handle_test' ) ); } /** * Ask smsportal.app for the connection state before the screen header is printed, so the header pill and * the overview show the same answer. render() reuses it: one request per page, also when the answer is * not cached (an error). * * @return void */ public static function prime(): void { self::$primed = Connection::is_connected() ? Api::connection_info() : null; } /** * Render the tab: the connect screen, or the state of the connection. * * @return void */ public static function render(): void { $info = self::$primed; self::$primed = null; $smsportal_connection = Connection::get(); if ( null === $smsportal_connection ) { $smsportal_connect_problem = Connection::connect_problem(); include SMSPORTAL_DIR . 'includes/Admin/views/overview-disconnected.php'; return; } $info = null === $info ? Api::connection_info() : $info; $smsportal_info = is_wp_error( $info ) ? null : $info; $smsportal_info_error = is_wp_error( $info ) ? $info->get_error_message() : ''; $smsportal_phone = self::phone_view( $smsportal_connection, $smsportal_info ); $smsportal_usage = self::usage_view( $smsportal_info ); $smsportal_local = Messages::month_counts(); $smsportal_rows = Messages::recent( self::RECENT_ROWS ); $smsportal_owner_phone = SettingsPage::owner_phone(); include SMSPORTAL_DIR . 'includes/Admin/views/overview-connected.php'; } /** * What the overview shows about the phone. * * @param array $connection Stored connection. * @param array|null $info Server answer, or null when unavailable. * @return array{known:bool,online:bool,name:string,details:string[],last_seen:string} */ public static function phone_view( array $connection, ?array $info ): array { $name = (string) ( $info['device_name'] ?? $connection['device_name'] ?? '' ); if ( null === $info ) { return array( 'known' => false, 'online' => false, 'name' => $name, 'details' => array(), 'last_seen' => '', ); } $details = array(); if ( isset( $info['battery_percent'] ) && is_numeric( $info['battery_percent'] ) ) { $details[] = ! empty( $info['charging'] ) /* translators: %d: battery level in percent. */ ? sprintf( __( 'battery %d%% (charging)', '85pl-android-sms-api' ), (int) $info['battery_percent'] ) /* translators: %d: battery level in percent. */ : sprintf( __( 'battery %d%%', '85pl-android-sms-api' ), (int) $info['battery_percent'] ); } $network = self::network_label( isset( $info['network_type'] ) && is_string( $info['network_type'] ) ? $info['network_type'] : '' ); if ( '' !== $network ) { $details[] = $network; } $heartbeat = isset( $info['last_heartbeat_at'] ) && is_string( $info['last_heartbeat_at'] ) ? strtotime( $info['last_heartbeat_at'] ) : false; return array( 'known' => true, 'online' => ! empty( $info['device_online'] ), 'name' => $name, 'details' => $details, /* translators: %s: time since the phone's last contact, e.g. "5 mins". */ 'last_seen' => false === $heartbeat ? '' : sprintf( __( 'last contact %s ago', '85pl-android-sms-api' ), human_time_diff( $heartbeat ) ), ); } /** * Account usage this month and whether the plan's limit is reached. * * @param array|null $info Server answer, or null when unavailable. * @return array{known:bool,site:int,account:int,limit:int,limit_reached:bool} */ public static function usage_view( ?array $info ): array { $limit = isset( $info['account_monthly_limit'] ) && is_numeric( $info['account_monthly_limit'] ) ? (int) $info['account_monthly_limit'] : -1; $account = isset( $info['account_sms_this_month'] ) && is_numeric( $info['account_sms_this_month'] ) ? (int) $info['account_sms_this_month'] : 0; return array( 'known' => null !== $info, 'site' => isset( $info['connection_sms_this_month'] ) && is_numeric( $info['connection_sms_this_month'] ) ? (int) $info['connection_sms_this_month'] : 0, 'account' => $account, 'limit' => $limit, 'limit_reached' => -1 !== $limit && $account >= $limit, ); } /** * Readable network type. * * @param string $type wifi, cellular, none or ''. * @return string */ private static function network_label( string $type ): string { $labels = array( 'wifi' => __( 'Wi-Fi', '85pl-android-sms-api' ), 'cellular' => __( 'Mobile data', '85pl-android-sms-api' ), 'none' => __( 'No internet', '85pl-android-sms-api' ), ); return $labels[ $type ] ?? ''; } /** * "Send a test SMS" handler (admin-post). * * @return void */ public static function handle_test(): void { if ( ! current_user_can( Menu::CAPABILITY ) ) { Guard::deny(); } check_admin_referer( self::TEST_ACTION ); self::send_test(); Guard::redirect( Menu::tab_url( 'overview' ) ); } /** * Queue a test SMS to the owner's number and say what happened. * * @return void */ public static function send_test(): void { $owner = SettingsPage::owner_phone(); if ( '' === $owner ) { Notice::add( 'error', __( 'Enter your phone number in Settings first: the test SMS goes there.', '85pl-android-sms-api' ) ); return; } $result = smsportal_send_sms( $owner, /* translators: %s: site name. */ sprintf( __( 'Test SMS from %s. smsportal.app works.', '85pl-android-sms-api' ), wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ) ), array( 'event_label' => __( 'Test SMS', '85pl-android-sms-api' ), 'expires_hours' => self::TEST_EXPIRES_HOURS, ) ); self::report_test( $result, $owner ); } /** * Notice about a queued test SMS. * * @param int|\WP_Error $result Result of smsportal_send_sms(). * @param string $owner Owner's number. * @return void */ public static function report_test( $result, string $owner ): void { if ( is_wp_error( $result ) ) { Notice::add( 'error', $result->get_error_message() ); return; } /* translators: %s: masked phone number. */ Notice::add( 'success', sprintf( __( 'Test SMS queued for %s. Its status is on the Sent tab.', '85pl-android-sms-api' ), Phone::mask( $owner ) ) ); } }