Skip to content

Commit 51b2bda

Browse files
pfefferleobenland
andauthored
Add type filter for feed based on ap_object_type taxonomy (#2510)
Co-authored-by: Konstantin Obenland <[email protected]>
1 parent 81de30a commit 51b2bda

File tree

9 files changed

+121
-10
lines changed

9 files changed

+121
-10
lines changed

build/social-web/feed-stage.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build/social-web/index.asset.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +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');
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' => '1896c8458ee5edc6becb');

build/social-web/index.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

includes/class-post-types.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,15 @@ function ( $params ) {
706706
'sanitize_callback' => 'absint',
707707
);
708708

709+
$params['ap_object_type'] = array(
710+
'description' => 'Filter posts by ActivityPub object type.',
711+
'type' => 'array',
712+
'items' => array(
713+
'type' => 'integer',
714+
'minimum' => 0,
715+
),
716+
);
717+
709718
return $params;
710719
}
711720
);
@@ -743,6 +752,19 @@ public static function filter_ap_post_by_user( $args, $request ) {
743752
'compare' => '=',
744753
);
745754

755+
// Filter by object type if provided.
756+
if ( ! empty( $request['ap_object_type'] ) ) {
757+
if ( ! isset( $args['tax_query'] ) ) {
758+
$args['tax_query'] = array(); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
759+
}
760+
761+
$args['tax_query'][] = array(
762+
'taxonomy' => 'ap_object_type',
763+
'field' => 'term_id',
764+
'terms' => $request['ap_object_type'],
765+
);
766+
}
767+
746768
return $args;
747769
}
748770

src/social-web/components/fields/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export * from './follow-status';
66
export * from './metadata';
77
export * from './modified';
88
export * from './name';
9+
export * from './object-type';
910
export * from './status';
1011
export * from './title';
1112
export * from './webfinger';
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { __ } from '@wordpress/i18n';
2+
import { resolveSelect } from '@wordpress/data';
3+
import { store as coreDataStore } from '@wordpress/core-data';
4+
import type { Term } from '@wordpress/core-data';
5+
import type { Field } from '@wordpress/dataviews';
6+
import type { FeedPost } from '../../../types';
7+
8+
export const objectTypeField: Field< FeedPost > = {
9+
id: 'ap_object_type',
10+
type: 'integer',
11+
label: __( 'Type', 'activitypub' ),
12+
enableHiding: false,
13+
enableSorting: false,
14+
getValue: ( { item }: { item: FeedPost } ): number => item.ap_object_type?.[ 0 ],
15+
getElements: async (): Promise< { value: number; label: string }[] > => {
16+
const translations: Record< string, string > = {
17+
// @see Base_Object::TYPES
18+
Article: __( 'Articles', 'activitypub' ),
19+
Audio: __( 'Music & Podcasts', 'activitypub' ),
20+
Document: __( 'Documents & Files', 'activitypub' ),
21+
Event: __( 'Events & Meetups', 'activitypub' ),
22+
Image: __( 'Photos & Images', 'activitypub' ),
23+
Note: __( 'Notes & Updates', 'activitypub' ),
24+
Page: __( 'Pages', 'activitypub' ),
25+
Place: __( 'Places & Locations', 'activitypub' ),
26+
Video: __( 'Videos', 'activitypub' ),
27+
};
28+
const records: Term[] = await resolveSelect( coreDataStore ).getEntityRecords( 'taxonomy', 'ap_object_type' );
29+
30+
if ( ! records ) {
31+
return [];
32+
}
33+
34+
// Map all terms with translations for known types
35+
return records.map( ( term: Term ): { value: number; label: string } => ( {
36+
value: term.id,
37+
label: translations[ term.name ] || term.name,
38+
} ) );
39+
},
40+
render: (): null => null,
41+
filterBy: {
42+
operators: [ 'is' ],
43+
},
44+
};

src/social-web/hooks/use-feed.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@ import { useEntityRecords } from '@wordpress/core-data';
22
import { useMemo } from '@wordpress/element';
33
import type { FeedPost } from '../types';
44

5+
interface Filter {
6+
field: string;
7+
operator: string;
8+
value: any;
9+
}
10+
511
interface UseFeedParams {
612
perPage?: number;
713
page?: number;
@@ -10,6 +16,7 @@ interface UseFeedParams {
1016
search?: string;
1117
userId?: number;
1218
fields?: string[];
19+
filters?: Filter[];
1320
}
1421

1522
interface UseFeedReturn {
@@ -27,7 +34,19 @@ export function useFeed( {
2734
order = 'desc',
2835
search = '',
2936
userId,
30-
fields = [ 'id', 'date', 'modified', 'title', 'excerpt', 'content', 'actor_info', 'status', 'link' ],
37+
fields = [
38+
'id',
39+
'date',
40+
'modified',
41+
'title',
42+
'excerpt',
43+
'content',
44+
'actor_info',
45+
'status',
46+
'link',
47+
'ap_object_type',
48+
],
49+
filters = [],
3150
}: UseFeedParams = {} ): UseFeedReturn {
3251
// Don't fetch if userId is not set
3352
const enabled = userId !== null && userId !== undefined;
@@ -47,8 +66,14 @@ export function useFeed( {
4766
args.user_id = userId;
4867
}
4968

69+
// Extract ap_object_type filter from filters array
70+
const apObjectTypeFilter = filters.find( ( f ) => f.field === 'ap_object_type' );
71+
if ( apObjectTypeFilter?.value !== undefined ) {
72+
args.ap_object_type = apObjectTypeFilter.value;
73+
}
74+
5075
return args;
51-
}, [ perPage, page, orderBy, order, search, userId, fields, enabled ] );
76+
}, [ perPage, page, orderBy, order, search, userId, fields, enabled, filters ] );
5277

5378
const { records, hasResolved, isResolving, totalItems, totalPages } = useEntityRecords< FeedPost >(
5479
'postType',

src/social-web/routes/feed/stage.tsx

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,14 @@ import { addQueryArgs, getQueryArgs } from '@wordpress/url';
1414
import { useSelect } from '@wordpress/data';
1515
import { Page } from '../../components/page';
1616
import { useFeed } from '../../hooks/use-feed';
17-
import { titleField, dateField, excerptField, metadataField, contentField } from '../../components/fields';
17+
import {
18+
titleField,
19+
dateField,
20+
excerptField,
21+
metadataField,
22+
contentField,
23+
objectTypeField,
24+
} from '../../components/fields';
1825
import { enforceContentExcerptMutualExclusion, normalizeFieldOrder } from './utils';
1926
import type { FeedPost } from '../../types';
2027
import { STORE_NAME } from '../../store';
@@ -120,9 +127,13 @@ export default function FeedStage( { onSelectItem }: FeedStageProps ) {
120127
const newFields = updatedView.fields || [];
121128
const fields = enforceContentExcerptMutualExclusion( oldFields, newFields );
122129

123-
updateView( { ...updatedView, fields } );
130+
// Reset to page 1 when filters change
131+
const filtersChanged = JSON.stringify( view.filters ) !== JSON.stringify( updatedView.filters );
132+
const page = filtersChanged ? 1 : updatedView.page;
133+
134+
updateView( { ...updatedView, fields, page } );
124135
},
125-
[ view.fields, updateView ]
136+
[ view.fields, view.filters, updateView ]
126137
);
127138

128139
// Reset view to default state when actor switches
@@ -145,10 +156,11 @@ export default function FeedStage( { onSelectItem }: FeedStageProps ) {
145156
order: view.sort?.direction || 'desc',
146157
search: view.search || '',
147158
userId: activeActorId,
159+
filters: view.filters,
148160
} );
149161

150162
const fields: Field< FeedPost >[] = useMemo(
151-
() => [ metadataField, titleField, excerptField, contentField, dateField ],
163+
() => [ metadataField, titleField, excerptField, contentField, dateField, objectTypeField ],
152164
[]
153165
);
154166

@@ -238,7 +250,13 @@ export default function FeedStage( { onSelectItem }: FeedStageProps ) {
238250
lastProcessedPage.current = currentPage;
239251
setIsLoadingMore( false );
240252
}
241-
}, [ feed, normalizedView.page, normalizedView.search, normalizedView.infiniteScrollEnabled ] );
253+
}, [
254+
feed,
255+
normalizedView.page,
256+
normalizedView.search,
257+
normalizedView.infiniteScrollEnabled,
258+
normalizedView.filters,
259+
] );
242260

243261
return (
244262
<Page

src/social-web/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ export interface FeedPost {
8686
};
8787
comment_status: string;
8888
ping_status: string;
89+
ap_object_type?: number[];
8990
actor_info?: ActorInfo;
9091
}
9192

0 commit comments

Comments
 (0)