Skip to content

Add Web code blocks #517

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions source/includes/steps-task-tracker-web-apollo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ content: |
component with the following code to create an ``ApolloClient`` object that
connects to your app:

.. literalinclude:: RealmApolloProvider.codeblock.realmApolloProvider.js
.. literalinclude:: /tutorial/generated/code/final/RealmApolloProvider.codeblock.realmApolloProvider.js
:caption: ``src/graphql/RealmApolloProvider.js``
:emphasize-lines: 2-6
---
Expand All @@ -20,12 +20,10 @@ content: |
GraphQL request must include an Authorization header that specifies a valid
user access token. The current client does not include any Authorization
headers, so all requests it makes will fail.

To fix this, update the ``createRealmApolloClient()`` function to include the
current user's access token in an Authorization header with every request:

.. literalinclude:: RealmApolloProvider.codeblock.createRealmApolloClient.js
.. literalinclude:: /tutorial/generated/code/final/RealmApolloProvider.codeblock.createRealmApolloClient.js
:caption: ``src/graphql/RealmApolloProvider.js``
:emphasize-lines: 4, 13

...
12 changes: 6 additions & 6 deletions source/includes/steps-task-tracker-web-realmapp.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ content: |
The app client is the primary interface to your Realm app from the SDK. In
``src/App.js``, replace ``"TODO"`` with your Realm App ID:

.. literalinclude:: App.codeblock.appID.js
.. literalinclude:: /tutorial/generated/code/final/App.codeblock.appID.js
:caption: ``src/App.js``

:language: javascript

.. admonition:: Use Your App ID
:class: note

Expand All @@ -32,7 +33,6 @@ content: |
currently use the app client you created. You need to update the functions to
actually call the SDK authentication and registration methods.

.. literalinclude:: final/realmAppProvider.js
:caption: ``src/RealmApp.tsx``
:emphasize-lines: 10, 16, 19

.. literalinclude:: /tutorial/generated/code/final/RealmApp.codeblock.realmAppProvider.js
:caption: ``src/RealmApp.js``
:language: javascript
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const APP_ID = "<your Realm app ID here>";
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const RequireLoggedInUser = ({ children }) => {
// Only render children if there is a logged in user.
const app = useRealmApp();
return app.currentUser ? children : <LoginScreen />;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
function useTeamMembers() {
const [teamMembers, setTeamMembers] = React.useState(null);
const [newUserEmailError, setNewUserEmailError] = React.useState(null);
const app = useRealmApp();
const { addTeamMember, removeTeamMember, getMyTeamMembers } = app.functions;
const updateTeamMembers = async () => {
const team = await getMyTeamMembers();
setTeamMembers(team);
};
/* eslint-disable react-hooks/exhaustive-deps */
React.useEffect(() => {
// display team members on load
updateTeamMembers();
}, []);
/* eslint-enable react-hooks/exhaustive-deps */
return {
teamMembers,
errorMessage: newUserEmailError,
addTeamMember: async (email) => {
const { error } = await addTeamMember(email);
if (error) {
setNewUserEmailError(error);
return { error };
} else {
updateTeamMembers();
}
},
removeTeamMember: async (email) => {
await removeTeamMember(email);
updateTeamMembers();
},
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const handleLogin = async () => {
setIsLoggingIn(true);
setError((e) => ({ ...e, password: null }));
try {
await app.logIn(Realm.Credentials.emailPassword(email, password));
} catch (err) {
handleAuthenticationError(err, setError);
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const handleRegistrationAndLogin = async () => {
const isValidEmailAddress = validator.isEmail(email);
setError((e) => ({ ...e, password: null }));
if (isValidEmailAddress) {
try {
// Register the user and, if successful, log them in
await app.emailPasswordAuth.registerUser(email, password);
return await handleLogin();
} catch (err) {
handleAuthenticationError(err, setError);
}
} else {
setError((err) => ({ ...err, email: "Email is invalid." }));
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const createRealmApolloClient = (app) => {
const link = new HttpLink({
// Realm apps use a standard GraphQL endpoint, identified by their App ID
uri: `https://realm.mongodb.com/api/client/v2.0/app/${app.id}/graphql`,
// A custom fetch handler adds the logged in user's access token to GraphQL requests
fetch: async (uri, options) => {
if (!app.currentUser) {
throw new Error(`Must be logged in to use the GraphQL API`);
}
// Refreshing a user's custom data also refreshes their access token
await app.currentUser.refreshCustomData();
// The handler adds a bearer token Authorization header to the otherwise unchanged request
options.headers.Authorization = `Bearer ${app.currentUser.accessToken}`;
return fetch(uri, options);
},
});

const cache = new InMemoryCache();

return new ApolloClient({ link, cache });
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default function RealmApolloProvider({ children }) {
const app = useRealmApp();
const [client, setClient] = React.useState(createRealmApolloClient(app));
React.useEffect(() => {
setClient(createRealmApolloClient(app));
}, [app]);
return <ApolloProvider client={client}>{children}</ApolloProvider>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
export const RealmAppProvider = ({ appId, children }) => {
const [app, setApp] = React.useState(new Realm.App(appId));
React.useEffect(() => {
setApp(new Realm.App(appId));
}, [appId]);

// Wrap the Realm.App object's user state with React state
const [currentUser, setCurrentUser] = React.useState(app.currentUser);
async function logIn(credentials) {
await app.logIn(credentials);
// If successful, app.currentUser is the user that just logged in
setCurrentUser(app.currentUser);
}
async function logOut() {
// Log out the currently active user
await app.currentUser?.logOut();
// If another user was logged in too, they're now the current user.
// Otherwise, app.currentUser is null.
setCurrentUser(app.currentUser);
}

const wrapped = { ...app, currentUser, logIn, logOut };

return (
<RealmAppContext.Provider value={wrapped}>
{children}
</RealmAppContext.Provider>
);
};
Original file line number Diff line number Diff line change
@@ -1 +1 @@
let app = App(id: "tasktracker-qczfq")
let app = App(id: "<your Realm app ID here>")
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
return (
<SidebarContainer>
<Card>
<SectionHeading>Projects</SectionHeading>
<SectionList>
{projects.map((project) => (
<SectionListItem
key={project.partition}
onClick={() => setCurrentProject(project)}
isSelected={project.partition === currentProject?.partition}
>
{project.name}
</SectionListItem>
))}
</SectionList>
<UserDetails
user={app.currentUser}
handleLogout={() => {
app.logOut();
}}
handleEditPermissions={() => {
setIsEditingPermissions(true);
}}
/>
</Card>
</SidebarContainer>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
const projects = useProjects();
const app = useRealmApp();
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Returns the shared instance of the Realm app.
export function getRealmApp() {
if (app === undefined) {
const appId = "tasktracker-qczfq"; // Set Realm app ID here.
const appId = "<your Realm app ID here>"; // Set Realm app ID here.
const appConfig = {
id: appId,
timeout: 10000,
Expand All @@ -13,4 +13,4 @@ export function getRealmApp() {
app = new Realm.App(appConfig);
}
return app;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
static schema = {
name: "Task",
properties: {
_id: "objectId",
_partition: "string?",
name: "string",
status: "string",
},
primaryKey: "_id",
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default function useProjects() {
const app = useRealmApp();
if (!app.currentUser) {
throw new Error("Cannot list projects if there is no logged in user.");
}
const projects = app.currentUser.customData.memberOf;
return projects;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const AddTaskMutation = gql`
mutation AddTask($task: TaskInsertInput!) {
addedTask: insertOneTask(data: $task) {
_id
_partition
name
status
}
}
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const DeleteTaskMutation = gql`
mutation DeleteTask($taskId: ObjectId!) {
deletedTask: deleteOneTask(query: { _id: taskId }) {
_id
_partition
name
status
}
}
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const UpdateTaskMutation = gql`
mutation UpdateTask($taskId: ObjectId!, $updates: TaskUpdateInput!) {
updatedTask: updateOneTask(query: { _id: $taskId }, set: $updates) {
_id
_partition
name
status
}
}
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
function useAddTask(project) {
const [addTaskMutation] = useMutation(AddTaskMutation, {
// Manually save added Tasks into the Apollo cache so that Task queries automatically update
// For details, refer to https://www.apollographql.com/docs/react/data/mutations/#making-all-other-cache-updates
update: (cache, { data: { addedTask } }) => {
cache.modify({
fields: {
tasks: (existingTasks = []) => [
...existingTasks,
cache.writeFragment({
data: addedTask,
fragment: TaskFieldsFragment,
}),
],
},
});
},
});

const addTask = async (task) => {
const { addedTask } = await addTaskMutation({
variables: {
task: {
_id: new ObjectId(),
_partition: project.partition,
status: "Open",
...task,
},
},
});
return addedTask;
};

return addTask;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
function useDeleteTask(project) {
const [deleteTaskMutation] = useMutation(DeleteTaskMutation);
const deleteTask = async (task) => {
const { deletedTask } = await deleteTaskMutation({
variables: { taskId: task._id },
});
return deletedTask;
};
return deleteTask;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
function useUpdateTask(project) {
const [updateTaskMutation] = useMutation(UpdateTaskMutation);
const updateTask = async (task, updates) => {
const { updatedTask } = await updateTaskMutation({
variables: { taskId: task._id, updates },
});
return updatedTask;
};
return updateTask;
}
Loading