- 13 Feb, 2020 3 commits
- 12 Feb, 2020 2 commits
-
-
Vincent authored
Because the parent components of the note elements also maintained the form state for the "new note" field, every time that field changed, the entire component and its child note components would also re-render. By splitting it up in a separate component, React can now just re-render that notes form field, which is way more performant with large notes.
-
Vincent authored
-
- 06 Feb, 2020 7 commits
- 09 Jan, 2020 1 commit
-
-
Vincent authored
-
- 08 Jan, 2020 10 commits
-
-
Vincent authored
-
Vincent authored
This should solve issues for systems using a font lacking the character we used before. Co-authored-by:
pmcb55 <patm@inrupt.com>
-
Vincent authored
-
Vincent authored
The only required code change was returning the _stored_ notes list when a new one is initialised - something that would ideally already have been the case.
-
Pat McBennett authored
-
Pat McBennett authored
- 04 Jan, 2020 3 commits
-
-
Vincent authored
-
Samuel Roze authored
- 28 Nov, 2019 1 commit
-
-
Vincent authored
This allows Notepod to collaborate with e.g. an app launcher to which the user is already logged in.
-
- 18 Nov, 2019 1 commit
-
-
Vincent authored
-
- 14 Nov, 2019 1 commit
-
-
Vincent authored
-
- 04 Nov, 2019 11 commits
-
-
Vincent authored
Markdown syntax is intended to be both human-readable, and parseable to apply basic formatting. Therefore, we can render regular plain text content as Markdown to display it a little prettier than a simple `<pre>` element.
-
Vincent authored
-
Vincent authored
-
Vincent authored
This, too, should not be too surprising: although the `removeSubject()` method is new, its purpose should be clear.
-
Vincent authored
-
Vincent authored
You should have the tools to do what we're doing now: add the ability to edit an existing Note.
-
Vincent authored
This final step is mostly to tie things together: it adds the ability to add new notes and view existing ones. From here, you should know enough to expand on the functionality: you could add the ability to add a title (a schema:headline?) to the note, or add other metadata such as the author (a schema:creator), or just take the things you've learned now and build a different type of web app altogether! The relevant code in this commit is in `addNote`. Given a note (a string), it initialises a new Subject with the note as its contents (through schema:text) and the current time as schema:dateCreated. Then, the <NotesList> component can use that date to sort the user's notes by date, from newest to oldest. So that's it for now! Study the code, start building your own web apps (you can usually find Vocabularies for your use cases at Linked Open Vocabularies [1]), and if you have questions, make sure to ask them at the Solid Forum [2]. [1] https://lov.linkeddata.es/dataset/lov/ [2] https://forum.solidproject.org/
-
Vincent authored
Now it's time to start working on some actual functionality: we'll be making an app that allows people to keep notes. With this commit, we'll prepare their Pod for our data model - much like how you might prepare database tables in a traditional application with a custom back-end. At a high level, this involves the following steps: 1. Check if there exists a Document to track notes in. 2. If it does not exist, create it. 3. Fetch that Document. So to start with the first step: in which Document should we track notes? The answer lies in the concept of a _Public Type Index_. The Public Type Index is itself a publicly accessible Document stored in the user's Pod. This Document contains a list of links to other Documents, along with the type of data that is to be included in those Documents. To store notes, the data type we will use is the `TextDigitalDocument`, defined by Schema.org [1]. Every time the user saves a note, we will store it as a TextDigitalDocument. If the Document containing these notes was located in the user's Pod at `/public/notes.ttl`, their Public Type Index could refer to it like this: | #notes | rdf:type | solid:TypeRegistration | | #notes | solid:forClass | schema:TextDigitalDocument | | #notes | solid:instance | /public/notes.ttl | The above Type Index includes one Type Registration, identified by `#notes`, that registers `/public/notes.ttl` for the data type `schema:TextDigitalDocument`. So how do we find the user's Public Type Index? It's usually listed in their profile, i.e. the Document accessible at their WebID: | #me | solid:publicTypeIndex | /settings/publicTypeIndex.ttl | This is a pattern you'll often encounter when writing Solid apps: you start with the user's WebID, read the profile Document located there, and from there you can find the data you need. So that's exactly what we do in this commit. In the `<Dashboard>` component, we want to list the number of movies the user has seen so far. Working backwards from there: 1. `<Dashboard>` calls the `useNotesList` Hook [2] to initiate the process of fetching existing reviews. 2. `useNotesList` retrieves a list of _every_ note the user has listed. `getNotes` can then filter the resulting Document to return only those that apply to elements of type `schema:TextDigitalDocument`. 3. `useNotesList` calls `usePublicTypeIndex` to get the Public Type Index. In there, it finds the URL of the Document containing notes, and returns that Document. 4. `usePublicTypeIndex` is a small Hook that calls `fetchPublicTypeIndex`, which retrieves the URL of the Public Type Index from the user's profile Document and fetches it. This is all well and good, but there's one potential snag: what if the Public Type Index does not list a Document to track notes in? To deal with this, `useNotesList` has one more trick up its sleeve: if it cannot find a Document that lists reviews, it calls `initialiseNotesList`, which creates a new file at `/public/notes.ttl` in the user's Pod, and adds it to their Public Type Index. If all the above sounds awfully complicated: that's because it is. Work is underway to make this easier in the future, allowing you to point a library to a data model, and having the library make sure that the user's Pod is prepared to handle that. For more information, see [3]. For now, though, you'll have to live with these convoluted instructions. [1] https://schema.org/TextDigitalDocument [2] https://reactjs.org/docs/hooks-intro.html [3] https://ruben.verborgh.org/blog/2019/06/17/shaping-linked-data-apps/
-
Vincent authored
Now we're getting started! In this commit, we fetch our first bit of data: the name of the current user. To explain how that works, I'll need to give you a crash course on Linked Data - but don't worry, I'll limit myself only to what is absolutely necessary to understand what's going on. In traditional back-ends, data is usually stored in database tables. When your back-end is a Solid Pod, however, data is stored in _Documents_ living at a certain URL - you can think of these as being similar to HTML documents living at a URL. When a person logs in to our app, we receive a _WebID_. A WebID can be used both to identify that user -since no other user shares that URL-, and as a pointer to a Document with information relevant to that user. My WebID is `https://vincentt.inrupt.net/profile/card#me`. That means that there is a Document at `https://vincentt.inrupt.net/profile/card` that contains data relevant to me. The most important thing to understand when writing a Solid app is how that data is represented. In a nutshell, a Document contains a list of relationships between things. For example, the Document referred to by my WebID contain the following relationships: | <some person> | has name | Vincent | | <some person> | works at | Inrupt | | <some person> | job title | Developer | (The common terminology is: every row contains a _Statement_, with in the first column the Statement's _Subject_, in the second column a _Predicate_, and the _Object_ in the third column.) You might notice that the table above is a description of `<some person>`: it's someone whose name is Vincent, who works at Inrupt and is a developer. However, `<some person>` is not really usable as a stable and unique identifier: for all we know, someone else might have a Document elsewhere that uses the exact same identifier. To solve this problem, Solid uses URLs as unique identifiers: after all, the Document that describes the entries already has a URL, and we can be sure that no other Document uses the same one. And like specific elements in an HTML document can be referred to by appending their ID to the document's URL, we can give elements we want to describe in a Document a unique identifier. As you might have been able to guess, instead of `<some person>` we can use `me` - hence the WebID `https://vincentt.inrupt.net/profile/card#me`! So now we might consider my Document to look as follows: | #me | has name | Vincent | | #me | works at | Inrupt | | #me | job title | Developer | But there's one more thing to consider: interoperability. An important tenet of Solid is being able to give multiple apps access to the same data: if I enter my name at service A, I don't want to have to re-enter it at service B. But if one service uses "name" to refer to a person's full name, whereas the other uses it to refer to a person's last name only, that would nip interoperbility in the bud. What's needed here is unique terms that have an agreed-upon definition. And just like we can have a Document describing me, we could also make Documents describing a term. And in fact, many people have done exactly that, for many different terms you might want to use. These Documents are called _Vocabularies_, and there's one for things you might want to put on a business card at http://www.w3.org/2006/vcard/ns — the _vCard_ Vocabulary. It contains Statements along the lines of: (As a shorthand for `http://www.w3.org/2006/vcard/ns#role`, we will use `vcard:role`.) So now we can use `vcard:role`, and be relatively confident that every other app using it will use it in the way described at that URL. We can combine terms from different Vocabularies, e.g. the FOAF ("Friend of a friend") vocabulary has a term to refer to a person's name at `http://xmlns.com/foaf/0.1/name`. My Document could thus look something like this: | #me | foaf:name | Vincent | | #me | vcard:organization-name | Inrupt | | #me | vcard:role | Developer | Everything that needs to be uniquely defined has a URL, with some _Literal_ values for the rest ("Vincent", "Inrupt", "Developer"). You could imagine "Inrupt" to be replaced by a URL as well, pointing to a Document describing the organisation itself - but for the intents and purposes of this tutorial, we will leave it at this. (It might give you some insight into why all this is referred to as "Linked Data" though!) A Document can describe multiple things. As an example, my Document could also have an entry for my address, and then link that to me: | #me | foaf:name | Vincent | | #me | vcard:organization-name | Inrupt | | #me | vcard:role | Developer | | #me | vcard:hasAddress | #address1 | | #address1 | vcard:street-address | 155 Country Lane | | #address1 | vcard:region | Cottingshire County | Phew! That should cover about all the Linked Data theory you should know to start working with Solid. So now let's see what's actually happening in this commit. The only user-visible change in this commit is that their name will be shown when they are logged in, and a foaf:name is present in their profile Document. So how is this done? The magic is in the newly added `fetchProfile` function. This function does two things: First, it requests the user's WebID from `solid-auth-client`. When the user has logged in using the authentication mechanism we added in an earlier commit, this will return their WebID, i.e. the URL to a Document container a representation of the user. Then, with the WebID in hand, it uses a library called _Tripledoc_ to retrieve that document's content. Then, `document.getSubject` returns a representation of all the Statements in that document that have the user's WebID as the Subject - so in the example table above, the rows with `#me` in the first column rather than `#address1`. `useProfile` is then called in the `<Dashboard>` component. Although `useProfile` provides access to all Statements about the user in their profile, we're currently only really interested in their name. We're going to use the `name` predicate of FOAF for this, so we're looking for a Statement like: | #me | http://xmlns.com/foaf/0.1/name | Vincent | Since `useProfile` returned all Statements with `#me` as the Subject, the Dashboard has access to it through `profile`. We can access the name by calling: const name = profile.getString('http://xmlns.com/foaf/0.1/name'); That returns a the user's name, if set to a literal string value, rather than a URL that points to a different Document. There are a number of Vocabularies that define terms used in many types of apps. To save you from having to copy-paste the URL of a Vocabulary time and time again, the `rdf-namespaces` package includes some useful helper objects for common Vocabularies — such as FOAF. Thus, the above can be shortened to: import { foaf } from 'rdf-namespaces'; const name = profile.getString(foaf.name); Finally, one disadvantage of Solid, as a developer, is that there are no guarantees about the data you want to access: you cannot be sure that a piece of data exists. The user might not have provided their name, or used a different Vocabulary to represent it. Thus, we should take into account that `profile.getString` might return `null`.
-
Vincent authored
To be able to write to a person's Pod, they need to log in and authorise our app. To do this, we use the React components in https://www.npmjs.com/package/@solid/react. Although it unfortunately comes with a bunch of stuff we will not be using, the authentication components are easy enough to use: wrap components that should be visible to people who are not logged in inside a <LoggedOut> component, and those that should only be visible to those who are logged in inside a <LoggedIn>. Actual authentication is initiated with a <LoginButton>. Unfortunately, at this time this involves including an HTML blob verbatim as downloaded from https://solid.github.io/solid-auth-client/dist/popup.html (this is linked to from the README of @solid/react). In the future, this will hopefully be properly packaged.
-
Vincent authored
This commit is not specific to building a Solid app, but it allows us to use the Bulma CSS framework: https://bulma.io This will allow us to use some basic styling without having to focus on it too much - distracting from the tutorial.
-