Vactory flag

Enable flag for various content types.


Vactory Flag simplifies the process for developers to easily enable flag features for various content types.

In this tutorial, we'll use Academy (vactory_academy) content as an example.

Below are the steps to follow

1. Enable the module with the following drush command

drush en vactory_decoupled_flag -y

2. Enabling flags for content type

Go to content type configuration /admin/structure/types/manage/vactory_academy and enable flags for this content type, as illustrated in the image below

3. Expose flag fields to the frontend

To enable flag features for any content type listing, add the following fields to the relevant widget configuration (widget of type json_api_collection)

  • has_flag: Indicates if the flag is enabled for this content type.
  • is_flagged: Indicates if a specific node is flagged by the current user.
collection:
type: json_api_collection
label: 'JSON:API'
options:
'#required': TRUE
'#default_value':
id: "vactory_academy_favorite"
resource: node--vactory_academy
filters:
- fields[node--vactory_academy]=is_flagged,has_flag,...

4. Adding the Flag in the Frontend

Receive the has_flag and is_flagged fields, then include them in the normalizer.

export const normalizeNode = (node) => {
return {
id: node?.drupal_internal__nid,
hasFlag: node?.has_flag,
isFlagged: node?.is_flagged,
...
}
}

In the relevant widget (card componenet), add the Flag component as follows

modules/academy/AcademyCard.jsx

import { Flag } from "@/ui"
export const AcademyCard = ({
title,
id,
isFlagged,
hasFlag,
}) => {
...
{hasFlag && (
<Flag
id={id}
title={title}
module="default_flag"
className="absolute right-4 top-4 cursor-pointer"
isFlagged={isFlagged}
reloadPage={reloadPage}
/>
)}
...
}

We can also include it on the detail page by adding the two fields to the node configuration

modules/academy/AcademyNode.jsx

export const config = {
id: "node--vactory_academy",
params: {
fields: {
"node--vactory_academy":
"has_flag,is_flagged,...",
},
...
},
}

5. Create a DF to list content flagged by the current user

Create a JSON:API collection for the desired content type and ensure you have added the following elements:

  • Expose our key fields, is_flagged and has_flag
  • Add this token to ensure the retrieval of only the flagged content by the current user [vactory:flagged_nodes:vactory_academy]
NB : replace 'vactory_academy' with your content type.
  • Include this cache configuration in your json_api_collection to make your listing cacheable per user and flagging entity
cache_tags:
- flagging_list
cache_contexts
- user

Here is the complete example

collection:
type: json_api_collection
label: 'JSON:API'
options:
'#required': TRUE
'#default_value':
id: "vactory_academy_favorite"
resource: node--vactory_academy
filters:
- fields[node--vactory_academy]=is_flagged,has_flag,...
...
- "[vactory:flagged_nodes:vactory_academy]"
cache_tags:
- flaggging_list
cache_contexts:
- user

After setting up the widget for this dynamic field on the frontend, define the 'reloadPage' function and pass it to the card componenet. This helps update the data when a user unflags content.

modules/academy/AcademyFavoriteWidget.jsx

const FavoriteAcademy = ({ data }) => {
const reloadPage = async () => {
const nids = await getCurrentUserFalggedArticles()
setFilters((prev) => {
let filters = {
...prev,
}
filters.page = {
...filters.page,
offset: 0,
}
filters.filter.internal_favourite = {
condition: {
path: "drupal_internal__nid",
operator: "IN",
value: nids?.nids,
},
}
return filters
})
}
const getCurrentUserFalggedArticles = async () => {
const response = await drupal.fetch(`api/flagging/all/vactory_academy`, {
withAuth: true,
method: "GET",
})
if (response.ok) {
return await response.json()
}
return []
}
useUpdateEffect(async () => {
const nids = await getCurrentUserFalggedArticles()
setFilters((prev) => {
let filters = {
...prev,
}
// Update pager.
filters.page = {
...filters.page,
offset: (pager - 1) * (filters?.page?.limit || defaulPagetLimit),
}
filters.filter.internal_favourite = {
condition: {
path: "drupal_internal__nid",
operator: "IN",
value: nids?.nids,
},
}
return filters
})
}, [pager])
return (
...
<AcademyCard {...post} reloadPage={reloadPage} />
...
)
}