const sanityClient = require('@sanity/client');
const imageUrlBuilder = require('@sanity/image-url');
// I store my query in another file for reusability with
// the `gatsby-source-plugin`
const modularQueries = require('../../../../sanityQueries/modularQuery.js');
import { removeWhitespace } from '../../../utils/strings';
import { normalizeSanityData } from './normalize';
let timeToFetchAfterMutation: any;
export const subscribeToData = (
pageId: string,
callback: () => any
) => {
const client = sanityClient({
projectId: process.env.SANITY_ID,
dataset: process.env.SANITY_DATASET,
useCdn: true,
withCredentials: true,
});
const query = `
*[_id == 'drafts.${pageId}']{
${removeWhitespace(modularQueries.modularQueryBody)}
}`;
client
.listen(query, {}, {includeResult: false})
.subscribe(() => {
clearTimeout(timeToFetchAfterMutation);
timeToFetchAfterMutation = setTimeout(() => {
callback();
}, 500);
});
};
export const fetchDataFromSanity = async (
pageId: string,
isDraft: boolean = false
) => {
const client = sanityClient({
projectId: process.env.SANITY_ID,
dataset: process.env.SANITY_DATASET,
useCdn: true,
// If it's a draft, we want to use credentials to
// access non-published documents. In order for this
// to work, you'll have to whitelist your domain!
withCredentials: isDraft,
});
const query = `
*[_id == '${isDraft ? `drafts.${pageId}` : pageId}']{
${removeWhitespace(modularQueries.modularQueryBody)}
}`;
const crudeData = await client.fetch(query);
if (crudeData && crudeData[0]) {
const builder = imageUrlBuilder(client);
const getImageURL = (image: any) => builder.image(image).url();
const normalizedData = await normalizeSanityData({
data: crudeData[0],
getImageURL,
});
if (normalizedData) {
return normalizedData;
}
} else {
return {
data: undefined,
getImageURL: undefined,
};
}
};
The only thing I do when normalizing the data is storing an imageUrl
property to image
objects in the data in order for them to show up in the front-end. All my image components are prepared to receive this ;)
export type TGetImageURL = (image: any) => any;
export interface ISanityDataGetImage {
getImageURL?: TGetImageURL;
data?: any;
}
const isImagelessObject = (field: any) => typeof field == 'object'
&& field._type !== 'image';
const isImage = (field: any) => {
return typeof field == 'object' &&
field._type &&
field._type === 'image' &&
field.asset;
}
const saveImage = async (field: any, getImageURL: TGetImageURL) => {
// Build the URL for the image using Sanity's package
const imageUrl = await getImageURL(field);
let newField = {...field};
if (imageUrl) {
newField = {
...field,
imageUrl,
}
} else {
console.error(`Erro em salvar uma imagem.`);
}
return newField;
};
const analyzeField = async (field: any, getImageURL: TGetImageURL) => {
let finalField = field;
for (const key of Object.keys(field)) {
let newField = field[key];
if (isImagelessObject(field[key])) {
// if it's an object without an image, we want to go deeper
// into its structure to check for images there
newField = await analyzeField(newField, getImageURL);
} else if (isImage(field[key])) {
// If it's an image field with an asset, save the image
newField = await saveImage(newField, getImageURL);
} else {
// If not an object, we simply skip this key
continue
}
// swap out the previous field with the new one
finalField = Object.assign(finalField, {
[key]: newField,
})
}
return finalField;
}
export const normalizeSanityData = ({data, getImageURL}: ISanityDataGetImage) => {
if (data && getImageURL) {
return analyzeField(data, getImageURL);
} else {
return undefined;
}
}