| @@ -598,8 +598,131 @@ | ||
| 598 | 598 | return rest_ensure_response( |
| 599 | 599 | array( |
| 600 | 600 | 'noteId' => (int) $post->ID, |
| 601 | 601 | 'postId' => (int) $new_post_id, |
| 602 | - 'editUrl' => (string) get_edit_post_link( $new_post_id, 'raw' ), | |
| 602 | + 'editUrl' => openstation_notes_draft_edit_url( (int) $new_post_id ), | |
| 603 | 603 | ) |
| 604 | 604 | ); |
| 605 | 605 | } |
| 606 | + | |
| 607 | +/** | |
| 608 | + * The admin edit URL for the draft a note became. | |
| 609 | + * | |
| 610 | + * `get_edit_post_link()` is the canonical answer, and it is filterable: | |
| 611 | + * a host can point it off-site (WordPress.com routes post edit links to | |
| 612 | + * its own editor) or a capability filter can blank it, and the client | |
| 613 | + * opens the URL inside a window that can only ever show THIS site's | |
| 614 | + * wp-admin. So this always answers with the on-site `post.php` URL the | |
| 615 | + * post type registers, whatever a filter made of the pretty one. | |
| 616 | + * | |
| 617 | + * @param int $post_id The draft post id. | |
| 618 | + * @return string Absolute admin URL, or '' when the post is gone. | |
| 619 | + */ | |
| 620 | +function openstation_notes_draft_edit_url( $post_id ) { | |
| 621 | + $draft = get_post( $post_id ); | |
| 622 | + if ( ! $draft instanceof WP_Post ) { | |
| 623 | + return ''; | |
| 624 | + } | |
| 625 | + $type_object = get_post_type_object( $draft->post_type ); | |
| 626 | + $edit_link = $type_object && ! empty( $type_object->_edit_link ) ? $type_object->_edit_link : 'post.php?post=%d'; | |
| 627 | + return admin_url( sprintf( $edit_link, $draft->ID ) . '&action=edit' ); | |
| 628 | +} | |
| 629 | + | |
| 630 | +/** | |
| 631 | + * Whether the current user's desktop would show any pinned notes. | |
| 632 | + * | |
| 633 | + * The presence hint the boot config ships as `hasNotes`: the notes | |
| 634 | + * bundle is presence-gated client-side (`src/notes/sentinel.ts`), so | |
| 635 | + * a user with no notes never downloads it — and never fires the | |
| 636 | + * boot-time list request the layer used to make unconditionally. | |
| 637 | + * | |
| 638 | + * **Steady-state cost: zero queries.** The computed answer is cached | |
| 639 | + * in user meta, stamped with a revision the site bumps whenever any | |
| 640 | + * note changes (`openstation_notes_bump_rev()` below). The revision | |
| 641 | + * lives in an autoloaded option and the user's meta cache is already | |
| 642 | + * primed on every admin request, so a boot between note changes | |
| 643 | + * reads two warm caches and touches the database not at all. Only | |
| 644 | + * the first boot after a note is created / deleted / re-scoped runs | |
| 645 | + * the probes again — two `fields => ids`, one-row queries at most: | |
| 646 | + * anyone's public note first, the user's own private ones second, | |
| 647 | + * mirroring the visibility rule the list route enforces. | |
| 648 | + * | |
| 649 | + * @return bool | |
| 650 | + */ | |
| 651 | +function openstation_notes_user_has_any() { | |
| 652 | + $rev = (string) get_option( 'desktop_mode_notes_rev', '0' ); | |
| 653 | + $user_id = get_current_user_id(); | |
| 654 | + $cached = (string) get_user_meta( $user_id, '_desktop_mode_has_notes', true ); | |
| 655 | + if ( '' !== $cached ) { | |
| 656 | + list( $cached_rev, $cached_value ) = array_pad( explode( ':', $cached, 2 ), 2, '' ); | |
| 657 | + if ( $cached_rev === $rev ) { | |
| 658 | + return '1' === $cached_value; | |
| 659 | + } | |
| 660 | + } | |
| 661 | + | |
| 662 | + $public = new WP_Query( | |
| 663 | + array( | |
| 664 | + 'post_type' => OPENSTATION_NOTES_POST_TYPE, | |
| 665 | + 'post_status' => 'publish', | |
| 666 | + 'posts_per_page' => 1, | |
| 667 | + 'fields' => 'ids', | |
| 668 | + 'no_found_rows' => true, | |
| 669 | + ) | |
| 670 | + ); | |
| 671 | + $has = (bool) $public->posts; | |
| 672 | + if ( ! $has ) { | |
| 673 | + $own = new WP_Query( | |
| 674 | + array( | |
| 675 | + 'post_type' => OPENSTATION_NOTES_POST_TYPE, | |
| 676 | + 'post_status' => 'private', | |
| 677 | + 'author' => $user_id, | |
| 678 | + 'posts_per_page' => 1, | |
| 679 | + 'fields' => 'ids', | |
| 680 | + 'no_found_rows' => true, | |
| 681 | + ) | |
| 682 | + ); | |
| 683 | + $has = (bool) $own->posts; | |
| 684 | + } | |
| 685 | + | |
| 686 | + update_user_meta( $user_id, '_desktop_mode_has_notes', $rev . ':' . ( $has ? '1' : '0' ) ); | |
| 687 | + return $has; | |
| 688 | +} | |
| 689 | + | |
| 690 | +/** | |
| 691 | + * Invalidate every user's cached `hasNotes` answer. | |
| 692 | + * | |
| 693 | + * One autoloaded revision counter instead of per-user cache deletes: | |
| 694 | + * a public note's existence changes the answer for EVERY user, and | |
| 695 | + * enumerating users to clear meta would be the expensive thing this | |
| 696 | + * cache exists to avoid. Bumping the rev makes all stamped answers | |
| 697 | + * stale at the cost of one option write per note change — and note | |
| 698 | + * changes are rare next to boots. | |
| 699 | + * | |
| 700 | + * @param int|WP_Post $post Post id or object being changed. | |
| 701 | + * @return void | |
| 702 | + */ | |
| 703 | +function openstation_notes_bump_rev( $post ) { | |
| 704 | + $post = get_post( $post ); | |
| 705 | + if ( ! $post instanceof WP_Post || OPENSTATION_NOTES_POST_TYPE !== $post->post_type ) { | |
| 706 | + return; | |
| 707 | + } | |
| 708 | + update_option( 'desktop_mode_notes_rev', (string) time() . '.' . wp_rand( 0, 999 ), true ); | |
| 709 | +} | |
| 710 | + | |
| 711 | +/** | |
| 712 | + * Every path a note can change through: status moves (create, | |
| 713 | + * publish/private flips, trash, restore) fire | |
| 714 | + * `transition_post_status`; hard deletes fire `deleted_post`. | |
| 715 | + * | |
| 716 | + * @param string $new_status New status. | |
| 717 | + * @param string $old_status Old status. | |
| 718 | + * @param WP_Post $post The post. | |
| 719 | + * @return void | |
| 720 | + */ | |
| 721 | +function openstation_notes_bump_rev_on_transition( $new_status, $old_status, $post ) { | |
| 722 | + if ( $new_status === $old_status ) { | |
| 723 | + return; | |
| 724 | + } | |
| 725 | + openstation_notes_bump_rev( $post ); | |
| 726 | +} | |
| 727 | +add_action( 'transition_post_status', 'openstation_notes_bump_rev_on_transition', 10, 3 ); | |
| 728 | +add_action( 'deleted_post', 'openstation_notes_bump_rev' ); | |