diff options
Diffstat (limited to 'src/client/apis')
| -rw-r--r-- | src/client/apis/HypothesisAuthenticationManager.tsx | 24 | ||||
| -rw-r--r-- | src/client/apis/hypothesis/HypothesisApiUtils.ts | 117 |
2 files changed, 131 insertions, 10 deletions
diff --git a/src/client/apis/HypothesisAuthenticationManager.tsx b/src/client/apis/HypothesisAuthenticationManager.tsx index c3e8d2fff..d4a81b3eb 100644 --- a/src/client/apis/HypothesisAuthenticationManager.tsx +++ b/src/client/apis/HypothesisAuthenticationManager.tsx @@ -6,6 +6,7 @@ import { Opt } from "../../fields/Doc"; import { Networking } from "../Network"; import "./HypothesisAuthenticationManager.scss"; import { Scripting } from "../util/Scripting"; +import { Hypothesis } from "./hypothesis/HypothesisApiUtils"; const prompt = "Paste authorization code here..."; @@ -18,7 +19,7 @@ export default class HypothesisAuthenticationManager extends React.Component<{}> @observable private showPasteTargetState = false; @observable private success: Opt<boolean> = undefined; @observable private displayLauncher = true; - @observable private credentials: string; + @observable private credentials: { username: string, apiKey: string } | undefined; private disposer: Opt<IReactionDisposer>; private set isOpen(value: boolean) { @@ -34,9 +35,10 @@ export default class HypothesisAuthenticationManager extends React.Component<{}> } public fetchAccessToken = async (displayIfFound = false) => { - const response: any = await Networking.FetchFromServer("/readHypothesisAccessToken"); + const jsonResponse = await Networking.FetchFromServer("/readHypothesisAccessToken"); + const response = jsonResponse !== "" ? JSON.parse(jsonResponse) : undefined; // if this is an authentication url, activate the UI to register the new access token - if (!response) { // new RegExp(AuthenticationUrl).test(response)) { + if (!response) { this.isOpen = true; this.authenticationLink = response; return new Promise<string>(async resolve => { @@ -44,12 +46,14 @@ export default class HypothesisAuthenticationManager extends React.Component<{}> this.disposer = reaction( () => this.authenticationCode, async authenticationCode => { - if (authenticationCode) { + const userProfile = authenticationCode && await Hypothesis.fetchUser(authenticationCode); + if (userProfile && userProfile.userid !== null) { this.disposer?.(); - Networking.PostToServer("/writeHypothesisAccessToken", { authenticationCode }); + const hypothesisUsername = Hypothesis.extractUsername(userProfile.userid); // extract username from profile + Networking.PostToServer("/writeHypothesisAccessToken", { authenticationCode, hypothesisUsername }); runInAction(() => { this.success = true; - this.credentials = response; + this.credentials = { username: hypothesisUsername, apiKey: authenticationCode! }; }); this.resetState(); resolve(authenticationCode); @@ -67,7 +71,7 @@ export default class HypothesisAuthenticationManager extends React.Component<{}> this.resetState(-1, -1); this.isOpen = true; } - return response.access_token; + return response; } resetState = action((visibleForMS: number = 3000, fadesOutInMS: number = 500) => { @@ -76,7 +80,7 @@ export default class HypothesisAuthenticationManager extends React.Component<{}> this.isOpen = false; this.success = undefined; this.displayLauncher = true; - this.credentials = ""; + this.credentials = undefined; this.shouldShowPasteTarget = false; this.authenticationCode = undefined; }); @@ -91,7 +95,7 @@ export default class HypothesisAuthenticationManager extends React.Component<{}> setTimeout(action(() => { this.success = undefined; this.displayLauncher = true; - this.credentials = ""; + this.credentials = undefined; }), fadesOutInMS); }), visibleForMS); } @@ -122,7 +126,7 @@ export default class HypothesisAuthenticationManager extends React.Component<{}> <> <span className={'welcome'} - >Welcome to Dash, {this.credentials} + >Welcome to Dash, {this.credentials.username} </span> <div className={'disconnect'} diff --git a/src/client/apis/hypothesis/HypothesisApiUtils.ts b/src/client/apis/hypothesis/HypothesisApiUtils.ts new file mode 100644 index 000000000..4c9fef45c --- /dev/null +++ b/src/client/apis/hypothesis/HypothesisApiUtils.ts @@ -0,0 +1,117 @@ +import { StrCast, Cast } from "../../../fields/Types"; +import HypothesisAuthenticationManager from "../HypothesisAuthenticationManager"; +import { SearchUtil } from "../../util/SearchUtil"; +import { action } from "mobx"; +import { Doc } from "../../../fields/Doc"; +import { DocumentType } from "../../documents/DocumentTypes"; + +export namespace Hypothesis { + + const getCredentials = async () => HypothesisAuthenticationManager.Instance.fetchAccessToken(); + + export const fetchAnnotation = async (annotationId: string) => { + const response = await fetch(`https://api.hypothes.is/api/annotations/${annotationId}`); + if (response.ok) { + return response.json(); + } else { + throw new Error('DASH: Error in fetchAnnotation GET request'); + } + }; + + /** + * Searches for annotations authored by the current user that contain @param searchKeyWord + */ + export const searchAnnotation = async (searchKeyWord: string) => { + const credentials = await getCredentials(); + const base = 'https://api.hypothes.is/api/search'; + const request = base + `?user=acct:${credentials.username}@hypothes.is&text=${searchKeyWord}`; + console.log("DASH Querying " + request); + const response = await fetch(request); + if (response.ok) { + return response.json(); + } else { + throw new Error('DASH: Error in searchAnnotation GET request'); + } + }; + + export const fetchUser = async (apiKey: string) => { + const response = await fetch('https://api.hypothes.is/api/profile', { + headers: { + 'Authorization': `Bearer ${apiKey}`, + }, + }); + if (response.ok) { + return response.json(); + } else { + throw new Error('DASH: Error in fetchUser GET request'); + } + }; + + export const editAnnotation = async (annotationId: string, newText: string) => { + console.log("DASH dispatching editRequest"); + const credentials = await getCredentials(); + document.dispatchEvent(new CustomEvent<{ newText: string, id: string, apiKey: string }>("editRequest", { + detail: { newText: newText, id: annotationId, apiKey: credentials.apiKey }, + bubbles: true + })); + }; + + /** + * Edit an annotation with ID @param annotationId to add a hyperlink to a Dash document, which needs to be + * written in the format [@param title](@param url) + */ + export const makeLink = async (title: string, url: string, annotationId: string) => { + const oldAnnotation = await fetchAnnotation(annotationId); + const oldText = StrCast(oldAnnotation.text); + const newHyperlink = `[${title}\n](${url})`; + const newText = oldText === "placeholder" ? newHyperlink : oldText + '\n\n' + newHyperlink; // if this is not the first link in the annotation, add link on new line + await editAnnotation(annotationId, newText); + }; + + /** + * Edit an annotation with ID @param annotationId to delete a hyperlink to the Dash document with URL @param linkUrl + */ + export const deleteLink = async (annotationId: string, linkUrl: string) => { + const annotation = await fetchAnnotation(annotationId); + const regex = new RegExp(`(\[[^[]*)\(${linkUrl.replace('/', '\/')}\)`); + const target = regex.exec(annotation.text); // use regex to extract the link to be deleted, which is written in [title](hyperlink) format + target && console.log(target); + // target && editAnnotation(annotationId, annotation.text.replace(target[0], '')); + }; + + // Finds the most recent placeholder annotation created and returns its ID + export const getPlaceholderId = async (searchKeyWord: string) => { + const getResponse = await Hypothesis.searchAnnotation(searchKeyWord); + const id = getResponse.rows.length > 0 ? getResponse.rows[0].id : undefined; + const uri = getResponse.rows.length > 0 ? getResponse.rows[0].uri : undefined; + return id ? { id, uri } : undefined; + }; + + // Construct an URL which will scroll the web page to a specific annotation's position + export const makeAnnotationUrl = (annotationId: string, baseUrl: string) => { + return `https://hyp.is/${annotationId}/${baseUrl}`; // embeds the generic version of Hypothes.is client, not the Dash version + // return baseUrl + '#annotations:' + annotationId; + }; + + // Extract username from Hypothe.is's userId format + export const extractUsername = (userid: string) => { + const regex = new RegExp('(?<=\:)(.*?)(?=\@)/'); + return regex.exec(userid)![0]; + }; + + // Return corres + export const getWebDocs = async (uri: string) => { + const results: Doc[] = []; + await SearchUtil.Search(uri, true).then(action(async (res: SearchUtil.DocSearchResult) => { + const docs = await Promise.all(res.docs.map(async doc => (await Cast(doc.extendsDoc, Doc)) || doc)); + const filteredDocs = docs.filter(doc => doc.type === DocumentType.WEB && doc.data); + + console.log("docs", docs); + console.log("FILTEREDDOCS: ", filteredDocs); + filteredDocs.forEach(doc => { + results.push(doc); + }); + })); + return results; + }; +}
\ No newline at end of file |
