✨Discover storytelling in the AI age with Pixar's Matthew Luhn at Sanity Connect, May 8th—register now

Discussion on syncing user data and customizing the Sanity studio based on user access.

14 replies
Last updated: Sep 13, 2022
Hi! I'm trying to create a document for all users like this: https://www.sanity.io/schemas/create-a-document-for-all-current-project-users-68b6f0db but I really want it to try and sync each time the studio loads. Any ideas on how to go about this?
I've tried doing this:
https://www.sanity.io/schemas/custom-default-desk-pane-35148d61 but it doesn't load with a custom desk structure. I've also tried making my desk structure into a React component but that doesn't work.
Sep 6, 2022, 5:01 PM
I think I found a way to do this, but it feels illegal:
Basically made the Desk structure async..


const DeskStructure = async () => {
  await syncUsers()
  return S.list()
}
Sep 6, 2022, 5:15 PM
Wait where do you want to get the user docs into? This seems indeed strange to do. Can you give me more input, so I can try understand what your goal is?
Sep 8, 2022, 2:04 PM
(this still makes me giggle btw: I think I found a way to do this, but it feels illegal)
Sep 8, 2022, 2:08 PM
Hi Saskia! I have a studio that runs 5 different frontend sites. We've organized it so that most documents (pages/articles etc) reference a "domain document" and fetch the correct frontend using multiple deployments in Vercel and environment variables to set the domain id.
Now we want to have a different view for the editors based on which page they can access. This is purely visual I know since no enterprise, but works well for us.

The jist is:
• Sync Sanity users to a document called 'sanityUser' on each studio load.
• The sanityUser document contains an array of references to domains, the "sanity id" of the user, as well as a "superadmin" boolean
• In the DeskStructure we then find the current users ID and find the corresponding document. We then filter the desk structure based on their access
Sep 8, 2022, 2:40 PM
If you are interested it looks like this:


// Desk structure
const DeskStructure = async () => {
  await syncUsers()
  const currentUser = await userStore.getCurrentUser()
  const { superAdmin, siteAccess } = await studioClient.fetch(
    `*[_type == 'sanityUser' && id == $id][0]{
      superAdmin,
      "siteAccess": siteAccess[]._ref
    }`,
    { id: currentUser.id }
  )

  if (!superAdmin) {
    return S.documentTypeList("domain")
      .filter("_id in $siteAccess")
      .params({ siteAccess })
      .title("Nettsider")
      .child((domainID) => domainDocumentList(domainID))
  }
  
  // Superadmin view
  return S.list() 
}

// Sync users
import client from "part:@sanity/base/client"
import cq from "concurrent-queue"
import userStore from "part:@sanity/base/user"
const studioClient = client.withConfig({ apiVersion: "2022-03-25" })

// Create a queue to limit the rate at which you write changes to Sanity
let queue = cq()
  .limit({ concurrency: 2 })
  .process(function (task) {
    return new Promise(function (resolve, reject) {
      setTimeout(resolve.bind(undefined, task), 1000)
    })
  })

const syncUsers = async () => {
  //query for all members, then use the userStore to get their details

  const [allUsers, existingUsers] = await Promise.all([
    studioClient.fetch(`*[_id in path('_.groups.*')].members[@ != 'everyone']`),
    studioClient.fetch(`*[_type == "sanityUser"].id`),
  ])

  // Filter to to the users that are not already existing
  const newUsers = allUsers
    .flat()
    .filter((user) => !existingUsers.includes(user))

  if (newUsers.length == 0) return

  // Fetch the user details from the userStore
  const userDetails = await userStore.getUsers(newUsers)

  for (const user of userDetails) {
    const doc = {
      _type: "sanityUser",
      _id: "sanityUser." + user.id,
      id: user.id,
      name: user.displayName,
    }
    queue(doc).then(async () => {
      //create the doc via the client
      studioClient
        .create(doc)
        .then((updatedDoc) => {
          console.log(
            `Hurray, the doc is updated! New document:${updatedDoc._id}`
          )
        })
        .catch((err) => {
          console.error("Oh no, the update failed: ", err.message)
        })
    })
  }
}

export default syncUsers
Sep 8, 2022, 2:43 PM
So it works haha. And I think this technique can be used to fully customize the studio based on document types etc 😄
Sep 8, 2022, 2:54 PM
I will def have a more detailed look on Monday and try out your approach 🫡If it's illegal we will both go to jail then
🚨 #partnersInCrime
Sep 11, 2022, 8:36 PM
I am just spitballing here, but maybe another approach (not sure if better or worse) would be to get some thing working in the stucture builder `hidden doc types`….

// /deskStructure.js

// Hide document types that we already have a structure definition for
const hiddenDocTypes = (listItem) =>
    ![
        "siteSettings",
        //some async functionality here

    ].includes(listItem.getId());

export default () =>
    S.list()
        .title('Content')
        .items([
            site1Docs,
            S.divider(),
            // your other desk partitions
            S.divider(),
            siteSettings,

            ...S.documentTypeListItems().filter(hiddenDocTypes)
        ])
Sep 13, 2022, 12:15 PM
This def works and is a solution we've used a lot, but it doesn't show a different view based on what user is logged in
Sep 13, 2022, 12:33 PM
i mean, that you could try the same kind of rendering which you have now, but use the userStore info … dont know if it works, but I am always for testing our performance…
Sep 13, 2022, 12:41 PM
And tbh, as long as it works, I would try stuff, thats the beauty of open source right?
Sep 13, 2022, 12:42 PM
Indeed, very happy with this discovery as this means we can basically make custom roles on the free plan
Sep 13, 2022, 12:45 PM
Everyone of course has the same access via the actual API etc but this will suit our projects nicely
Sep 13, 2022, 12:46 PM
yeah its more render access, but a workaround.
Sep 13, 2022, 1:12 PM

Sanity– build remarkable experiences at scale

Sanity is a modern headless CMS that treats content as data to power your digital business. Free to get started, and pay-as-you-go on all plans.

Was this answer helpful?