Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 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
2 changes: 1 addition & 1 deletion build/social-web/feed-stage.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/social-web/index.asset.php
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<?php return array('dependencies' => array('react', 'react-dom', 'react-jsx-runtime', 'wp-commands', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-data-controls', 'wp-date', 'wp-dom', 'wp-element', 'wp-html-entities', 'wp-i18n', 'wp-keyboard-shortcuts', 'wp-keycodes', 'wp-notices', 'wp-preferences', 'wp-primitives', 'wp-private-apis', 'wp-url', 'wp-warning'), 'version' => '658866a5e77c1bf57d37');
<?php return array('dependencies' => array('react', 'react-dom', 'react-jsx-runtime', 'wp-commands', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-data-controls', 'wp-date', 'wp-dom', 'wp-element', 'wp-html-entities', 'wp-i18n', 'wp-keyboard-shortcuts', 'wp-keycodes', 'wp-notices', 'wp-preferences', 'wp-primitives', 'wp-private-apis', 'wp-url', 'wp-warning'), 'version' => '1896c8458ee5edc6becb');
2 changes: 1 addition & 1 deletion build/social-web/index.js

Large diffs are not rendered by default.

24 changes: 24 additions & 0 deletions includes/class-post-types.php
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,17 @@ function ( $params ) {
'sanitize_callback' => 'absint',
);

$params['ap_object_type'] = array(
'description' => __( 'Filter posts by ActivityPub object type.', 'activitypub' ),
'type' => 'array',
'items' => array(
'type' => 'integer',
),
'sanitize_callback' => function ( $value ) {
return array_map( 'absint', (array) $value );
},
);

return $params;
}
);
Expand Down Expand Up @@ -743,6 +754,19 @@ public static function filter_ap_post_by_user( $args, $request ) {
'compare' => '=',
);

// Filter by object type if provided.
if ( ! empty( $request['ap_object_type'] ) ) {
if ( ! isset( $args['tax_query'] ) ) {
$args['tax_query'] = array(); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
}

$args['tax_query'][] = array(
'taxonomy' => 'ap_object_type',
'field' => 'term_id',
'terms' => $request['ap_object_type'],
);
}

return $args;
}

Expand Down
1 change: 1 addition & 0 deletions src/social-web/components/fields/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export * from './follow-status';
export * from './metadata';
export * from './modified';
export * from './name';
export * from './object-type';
export * from './status';
export * from './title';
export * from './webfinger';
32 changes: 32 additions & 0 deletions src/social-web/components/fields/object-type/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { __ } from '@wordpress/i18n';
import type { Field } from '@wordpress/dataviews';
import type { FeedPost } from '../../../types';

/**
* Object type filter field
*
* @param apObjectTypes - Array of taxonomy terms from ap_object_type
* @return Field configuration for object type filtering
*/
export function objectTypeField( apObjectTypes?: any[] ): Field< FeedPost > {
return {
id: 'ap_object_type',
label: __( 'Type', 'activitypub' ),
enableHiding: false,
enableSorting: false,
elements:
apObjectTypes?.map( ( term ) => ( {
value: term.id,
label: term.name,
} ) ) || [],
getValue: ( { item } ) => item.ap_object_type?.[ 0 ],
render: ( { item } ) => {
const termId = item.ap_object_type?.[ 0 ];
const term = apObjectTypes?.find( ( t ) => t.id === termId );
return <span>{ term?.name || '—' }</span>;
},
filterBy: {
operators: [ 'isAny' ],
},
};
}
29 changes: 27 additions & 2 deletions src/social-web/hooks/use-feed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ import { useEntityRecords } from '@wordpress/core-data';
import { useMemo } from '@wordpress/element';
import type { FeedPost } from '../types';

interface Filter {
field: string;
operator: string;
value: any;
}

interface UseFeedParams {
perPage?: number;
page?: number;
Expand All @@ -10,6 +16,7 @@ interface UseFeedParams {
search?: string;
userId?: number;
fields?: string[];
filters?: Filter[];
}

interface UseFeedReturn {
Expand All @@ -27,7 +34,19 @@ export function useFeed( {
order = 'desc',
search = '',
userId,
fields = [ 'id', 'date', 'modified', 'title', 'excerpt', 'content', 'actor_info', 'status', 'link' ],
fields = [
'id',
'date',
'modified',
'title',
'excerpt',
'content',
'actor_info',
'status',
'link',
'ap_object_type',
],
filters = [],
}: UseFeedParams = {} ): UseFeedReturn {
// Don't fetch if userId is not set
const enabled = userId !== null && userId !== undefined;
Expand All @@ -47,8 +66,14 @@ export function useFeed( {
args.user_id = userId;
}

// Extract ap_object_type filter from filters array
const apObjectTypeFilter = filters.find( ( f ) => f.field === 'ap_object_type' );
if ( apObjectTypeFilter && apObjectTypeFilter.value && apObjectTypeFilter.value.length > 0 ) {
args.ap_object_type = apObjectTypeFilter.value;
}

return args;
}, [ perPage, page, orderBy, order, search, userId, fields, enabled ] );
}, [ perPage, page, orderBy, order, search, userId, fields, enabled, filters ] );

const { records, hasResolved, isResolving, totalItems, totalPages } = useEntityRecords< FeedPost >(
'postType',
Expand Down
37 changes: 31 additions & 6 deletions src/social-web/routes/feed/stage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,17 @@ import type { View, Field } from '@wordpress/dataviews';
import { __ } from '@wordpress/i18n';
import { addQueryArgs, getQueryArgs } from '@wordpress/url';
import { useSelect } from '@wordpress/data';
import { useEntityRecords } from '@wordpress/core-data';
import { Page } from '../../components/page';
import { useFeed } from '../../hooks/use-feed';
import { titleField, dateField, excerptField, metadataField, contentField } from '../../components/fields';
import {
titleField,
dateField,
excerptField,
metadataField,
contentField,
objectTypeField,
} from '../../components/fields';
import { enforceContentExcerptMutualExclusion, normalizeFieldOrder } from './utils';
import type { FeedPost } from '../../types';
import { STORE_NAME } from '../../store';
Expand Down Expand Up @@ -53,6 +61,9 @@ export default function FeedStage( { onSelectItem }: FeedStageProps ) {
[]
);

// Fetch ap_object_type taxonomy terms for filter
const { records: apObjectTypes } = useEntityRecords( 'taxonomy', 'ap_object_type' );

// Track URL query parameters as state for reactivity
const [ urlQueryParams, setUrlQueryParams ] = useState( () => {
const args = getQueryArgs( window.location.href ) as {
Expand Down Expand Up @@ -120,9 +131,13 @@ export default function FeedStage( { onSelectItem }: FeedStageProps ) {
const newFields = updatedView.fields || [];
const fields = enforceContentExcerptMutualExclusion( oldFields, newFields );

updateView( { ...updatedView, fields } );
// Reset to page 1 when filters change
const filtersChanged = JSON.stringify( view.filters ) !== JSON.stringify( updatedView.filters );
const page = filtersChanged ? 1 : updatedView.page;

updateView( { ...updatedView, fields, page } );
},
[ view.fields, updateView ]
[ view.fields, view.filters, updateView ]
);

// Reset view to default state when actor switches
Expand All @@ -145,11 +160,15 @@ export default function FeedStage( { onSelectItem }: FeedStageProps ) {
order: view.sort?.direction || 'desc',
search: view.search || '',
userId: activeActorId,
filters: view.filters,
} );

// Create ap_object_type filter field
const apObjectTypeField = useMemo( () => objectTypeField( apObjectTypes ), [ apObjectTypes ] );

const fields: Field< FeedPost >[] = useMemo(
() => [ metadataField, titleField, excerptField, contentField, dateField ],
[]
() => [ metadataField, titleField, excerptField, contentField, dateField, apObjectTypeField ],
[ apObjectTypeField ]
);

// Normalize view.fields to maintain the canonical order defined in fields array
Expand Down Expand Up @@ -238,7 +257,13 @@ export default function FeedStage( { onSelectItem }: FeedStageProps ) {
lastProcessedPage.current = currentPage;
setIsLoadingMore( false );
}
}, [ feed, normalizedView.page, normalizedView.search, normalizedView.infiniteScrollEnabled ] );
}, [
feed,
normalizedView.page,
normalizedView.search,
normalizedView.infiniteScrollEnabled,
normalizedView.filters,
] );

return (
<Page
Expand Down
1 change: 1 addition & 0 deletions src/social-web/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export interface FeedPost {
};
comment_status: string;
ping_status: string;
ap_object_type?: number[];
actor_info?: ActorInfo;
}

Expand Down