Browse Source

fix: prompt user on actions with no permission

liyasthomas 4 years ago
parent
commit
94763dcb31

+ 20 - 5
packages/hoppscotch-app/components/teams/Team.vue

@@ -2,11 +2,20 @@
   <div class="border border-divider rounded flex flex-col flex-1">
     <div
       class="flex flex-1 items-start"
-      :class="{
-        'cursor-pointer hover:bg-primaryDark transition hover:border-dividerDark focus-visible:border-dividerDark':
-          compact && team.myRole === 'OWNER',
-      }"
-      @click="compact && team.myRole === 'OWNER' ? $emit('invite-team') : ''"
+      :class="
+        compact
+          ? team.myRole === 'OWNER'
+            ? 'cursor-pointer hover:bg-primaryDark transition hover:border-dividerDark focus-visible:border-dividerDark'
+            : 'cursor-not-allowed bg-primaryLight'
+          : ''
+      "
+      @click="
+        compact
+          ? team.myRole === 'OWNER'
+            ? $emit('invite-team')
+            : noPermission()
+          : ''
+      "
     >
       <div class="p-4">
         <label
@@ -182,4 +191,10 @@ const exitTeam = () => {
     )
   )() // Tasks (and TEs) are lazy, so call the function returned
 }
+
+const noPermission = () => {
+  $toast.error(t("profile.no_permission").toString(), {
+    icon: "error_outline",
+  })
+}
 </script>

+ 57 - 56
packages/hoppscotch-app/helpers/backend/GQLClient.ts

@@ -69,61 +69,63 @@ authIdToken$.subscribe(() => {
   subscriptionClient.client.close()
 })
 
-const createHoppClient = () => createClient({
-  url: BACKEND_GQL_URL,
-  exchanges: [
-    devtoolsExchange,
-    dedupExchange,
-    offlineExchange({
-      schema: schema as any,
-      keys: keyDefs,
-      optimistic: optimisticDefs,
-      updates: updatesDef,
-      resolvers: resolversDef,
-      storage,
-    }),
-    authExchange({
-      addAuthToOperation({ authState, operation }) {
-        if (!authState || !authState.authToken) {
-          return operation
-        }
-
-        const fetchOptions =
-          typeof operation.context.fetchOptions === "function"
-            ? operation.context.fetchOptions()
-            : operation.context.fetchOptions || {}
-
-        return makeOperation(operation.kind, operation, {
-          ...operation.context,
-          fetchOptions: {
-            ...fetchOptions,
-            headers: {
-              ...fetchOptions.headers,
-              Authorization: `Bearer ${authState.authToken}`,
+const createHoppClient = () =>
+  createClient({
+    url: BACKEND_GQL_URL,
+    exchanges: [
+      devtoolsExchange,
+      dedupExchange,
+      offlineExchange({
+        schema: schema as any,
+        keys: keyDefs,
+        optimistic: optimisticDefs,
+        updates: updatesDef,
+        resolvers: resolversDef,
+        storage,
+      }),
+      authExchange({
+        addAuthToOperation({ authState, operation }) {
+          if (!authState || !authState.authToken) {
+            return operation
+          }
+
+          const fetchOptions =
+            typeof operation.context.fetchOptions === "function"
+              ? operation.context.fetchOptions()
+              : operation.context.fetchOptions || {}
+
+          return makeOperation(operation.kind, operation, {
+            ...operation.context,
+            fetchOptions: {
+              ...fetchOptions,
+              headers: {
+                ...fetchOptions.headers,
+                Authorization: `Bearer ${authState.authToken}`,
+              },
             },
-          },
-        })
-      },
-      willAuthError({ authState }) {
-        return !authState || !authState.authToken
-      },
-      getAuth: async () => {
-        if (!probableUser$.value) return { authToken: null }
-
-        await waitProbableLoginToConfirm()
-
-        return {
-          authToken: getAuthIDToken(),
-        }
-      },
-    }),
-    fetchExchange,
-    subscriptionExchange({
-      // @ts-expect-error: An issue with the Urql typing
-      forwardSubscription: (operation) => subscriptionClient.request(operation),
-    }),
-  ],
-})
+          })
+        },
+        willAuthError({ authState }) {
+          return !authState || !authState.authToken
+        },
+        getAuth: async () => {
+          if (!probableUser$.value) return { authToken: null }
+
+          await waitProbableLoginToConfirm()
+
+          return {
+            authToken: getAuthIDToken(),
+          }
+        },
+      }),
+      fetchExchange,
+      subscriptionExchange({
+        // @ts-expect-error: An issue with the Urql typing
+        forwardSubscription: (operation) =>
+          subscriptionClient.request(operation),
+      }),
+    ],
+  })
 
 export const client = ref(createHoppClient())
 
@@ -312,8 +314,7 @@ export const runMutation = <
   pipe(
     TE.tryCatch(
       () =>
-        client
-          .value
+        client.value
           .mutation(mutation, variables, {
             requestPolicy: "cache-and-network",
             ...additionalConfig,

+ 2 - 3
packages/hoppscotch-app/helpers/backend/caching/updates.ts

@@ -108,19 +108,18 @@ export const updatesDef: GraphCacheUpdaters = {
     },
     removeTeamMember: (_result, { teamID, userUid }, cache) => {
       const newMembers = (
-        cache.resolve(
+        (cache.resolve(
           {
             __typename: "Team",
             id: teamID,
           },
           "teamMembers"
-        ) as string[]
+        ) as string[]) ?? []
       )
         .map((x) => [x, cache.resolve(x, "user") as string])
         .map(([key, userKey]) => [key, cache.resolve(userKey, "uid") as string])
         .filter(([_key, uid]) => uid !== userUid)
         .map(([key]) => key)
-
       cache.link({ __typename: "Team", id: teamID }, "teamMembers", newMembers)
     },
     createTeamInvitation: (result, _args, cache, _info) => {

+ 11 - 10
packages/hoppscotch-app/locales/en.json

@@ -261,6 +261,17 @@
     "script": "Pre-Request Script",
     "snippets": "Snippets"
   },
+  "profile": {
+    "editor": "Editor",
+    "editor_description": "Editors can add, edit, and delete requests.",
+    "no_permission": "You do not have permission to perform this action.",
+    "owner": "Owner",
+    "owner_description": "Owners can add, edit, and delete requests, collections and team members.",
+    "roles": "Roles",
+    "roles_description": "Roles are used to control access to the shared collections.",
+    "viewer": "Viewer",
+    "viewer_description": "Viewers can only view and use requests."
+  },
   "remove": {
     "star": "Remove star"
   },
@@ -310,16 +321,6 @@
     "waiting_for_connection": "waiting for connection",
     "xml": "XML"
   },
-  "profile": {
-    "roles": "Roles",
-    "roles_description": "Roles are used to control access to the shared collections.",
-    "owner": "Owner",
-    "owner_description": "Owners can add, edit, and delete requests, collections and team members.",
-    "editor": "Editor",
-    "editor_description": "Editors can add, edit, and delete requests.",
-    "viewer": "Viewer",
-    "viewer_description": "Viewers can only view and use requests."
-  },
   "settings": {
     "accent_color": "Accent color",
     "account": "Account",