Skip to content

Commit

Permalink
Merge pull request #25 from weichenw/add-group
Browse files Browse the repository at this point in the history
Add group selections in settings
  • Loading branch information
weichenw authored Dec 16, 2021
2 parents acb3637 + dfdf559 commit 56f99da
Show file tree
Hide file tree
Showing 15 changed files with 443 additions and 155 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Use Hypothesis icon on the side icon ribbon or command to trigger manual sync.
- `Highlights folder`: Specify the folder location for your Hypothesis articles
- `Sync on startup`: Automatically sync highlights when open Obsidian
- `Highlights template`: Nunjuck template for rendering your highlights
- `Groups`: Add/remove group to be synced
- `Reset sync`: Wipe your sync history. Does not delete any previously synced highlights from your vault

### To sync all new highlights since previous update
Expand Down
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "obsidian-hypothesis-plugin",
"name": "Hypothes.is",
"version": "0.1.12",
"version": "0.1.13",
"minAppVersion": "0.11.0",
"description": "Sync your Hypothesis highlights",
"author": "weichenw",
Expand Down
226 changes: 127 additions & 99 deletions src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,136 +2,164 @@
import { Notice, moment } from 'obsidian';

export default class ApiManager {
readonly baseUrl: string = 'https://hypothes.is/api';
private token: string;
private userid: string;
readonly baseUrl: string = 'https://hypothes.is/api';
private token: string;
private userid: string;

constructor(token: string, userid: string = undefined){
constructor(token: string, userid: string = undefined) {
this.token = token;
this.userid = userid;
}

private getHeaders(){
private getHeaders() {
return {
'AUTHORIZATION': `Bearer ${this.token}`,
'Accept': 'application/json',
};
}

async getProfile(){
let response;
let data;
async getProfile() {
let response;
let data;

try {
response = await fetch(`${this.baseUrl}/profile`, { headers: { ...this.getHeaders() } })
}
catch (e) {
new Notice('Authorization failed. Please check your API token and try again.')
console.error("Failed to fetch profile : ", e);
return;
}

if (response && response.ok) {
data = await response.json();
} else {
new Notice('Authorization failed. Please check your API token and try again.')
console.error("Failed to fetch profile : ", response);
return;
}

if (data.userid) {
return data.userid;
} else {
//user not found
new Notice('User not found. Please check your API token and try again.')
}
}

try{
response = await fetch(`${this.baseUrl}/profile`, {headers: {...this.getHeaders()}})
}
catch (e) {
new Notice('Authorization failed. Please check your API token and try again.')
console.error("Failed to fetch profile : ", e);
return;
async getHighlights(lastSyncDate?: Date) {
// let offset = 0;
let maxResult = 1000;
let initialQuery = true;
let result = [];
let response;

const queryDate = lastSyncDate ? `&search_after=${moment.utc(lastSyncDate).format()}` : '';
// const limit = lastSyncDate ? 200 : 100;
const limit = 200;

for (let resultCount = 0; resultCount < maxResult;) {

try {
response = await fetch(`${this.baseUrl}/search?user=${this.userid}&offset=${resultCount}&limit=${limit}&sort=updated&order=asc` + queryDate, { headers: { ...this.getHeaders() } })
}
catch (e) {
new Notice('Error occurs. Please check your API token and try again.')
console.log("Failed to fetch highlights : ", e);
return;
}

if (response && response.ok) {
const data = await response.json();

if (!data.rows.length) {
break;
}

if (response && response.ok) {
data = await response.json();
} else {
new Notice('Authorization failed. Please check your API token and try again.')
console.error("Failed to fetch profile : ", response);
return;
//initial run to set total
if (initialQuery) {
if (data.total <= maxResult) {
//overwrite total
maxResult = data.total;
//no longer inital query to find total
initialQuery = !initialQuery;
}
}

if (data.userid) {
return data.userid;
} else {
//user not found
new Notice('User not found. Please check your API token and try again.')
resultCount += data.rows.length;
result = [...result, ...data.rows];

//break loop. pagination doesnt work with search_after param
if (lastSyncDate) {
break;
}
}

async getHighlights(lastSyncDate?: Date){
// let offset = 0;
let maxResult = 1000;
let initialQuery = true;
let result= [];
let response;
} else {
new Notice('Sync failed. Please check your API token and try again.')
console.log("Failed to fetch highlights : ", response);
return;
}

const queryDate = lastSyncDate ? `&search_after=${moment.utc(lastSyncDate).format()}` : '';
// const limit = lastSyncDate ? 200 : 100;
const limit = 200;

for(let resultCount = 0; resultCount < maxResult;){
}
return result;
}

try{
response = await fetch(`${this.baseUrl}/search?user=${this.userid}&offset=${resultCount}&limit=${limit}&sort=updated&order=asc`+queryDate, {headers: {...this.getHeaders()}})
}
catch (e) {
new Notice('Error occurs. Please check your API token and try again.')
console.log("Failed to fetch highlights : ", e);
return;
}
async getHighlightWithUri(uri: string) {

if (response && response.ok) {
const data = await response.json();

if(!data.rows.length){
break;
}

//initial run to set total
if(initialQuery){
if(data.total <= maxResult){
//overwrite total
maxResult = data.total;
//no longer inital query to find total
initialQuery = !initialQuery;
}
}

resultCount += data.rows.length;
result = [...result, ...data.rows];

//break loop. pagination doesnt work with search_after param
if(lastSyncDate){
break;
}

} else {
new Notice('Sync failed. Please check your API token and try again.')
console.log("Failed to fetch highlights : ", response);
return;
}
let result = [];
let response;
const limit = 200;

try {
response = await fetch(`${this.baseUrl}/search?user=${this.userid}&limit=${limit}&uri=${uri}`, { headers: { ...this.getHeaders() } })
}
catch (e) {
new Notice('Error occurs. Please check your API token and try again.')
console.log("Failed to fetch highlights : ", e);
return;
}

}
return result;
}
if (response && response.ok) {
const data = await response.json();

async getHighlightWithUri(uri: string){
result = data.rows;

let result= [];
let response;
const limit = 200;
} else {
new Notice('Sync failed. Please check your API token and try again.')
console.log("Failed to fetch highlights : ", response);
return;
}

try{
response = await fetch(`${this.baseUrl}/search?user=${this.userid}&limit=${limit}&uri=${uri}`, {headers: {...this.getHeaders()}})
}
catch (e) {
new Notice('Error occurs. Please check your API token and try again.')
console.log("Failed to fetch highlights : ", e);
return;
}
return result;

if (response && response.ok) {
const data = await response.json();
}

result = data.rows;
async getGroups() {
let result = [];
let response;

} else {
new Notice('Sync failed. Please check your API token and try again.')
console.log("Failed to fetch highlights : ", response);
return;
}
try {
response = await fetch(`${this.baseUrl}/groups`, { headers: { ...this.getHeaders() } })
}
catch (e) {
new Notice('Error occurs. Please check your API token and try again.')
console.log("Failed to fetch groups : ", e);
return;
}

if (response && response.ok) {
const data = await response.json();

return result;
result = data;

} else {
new Notice('Sync failed. Please check your API token and try again.')
console.log("Failed to fetch groups : ", response);
return;
}

return result;
}

}
4 changes: 2 additions & 2 deletions src/assets/defaultTemplate.njk
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
- Category: #article{% endif %}

{% if is_new_article -%}## Highlights{%- endif %}
{% for highlight in highlights -%}- {{highlight.text}} — [Updated on {{highlight.updated}}]({{highlight.incontext}})
{% if highlight.tags | length %} - Tags: {% for tag in highlight.tags -%}#{{tag| replace(" ", "-")}} {%- endfor %}{%- endif %}
{% for highlight in highlights -%}- {{highlight.text}} — [Updated on {{highlight.updated}}]({{highlight.incontext}}) {% if 'Private' != highlight.group %} — Group: #{{highlight.group| replace(" ", "-")}}{%- endif %}
{% if highlight.tags | length %} - Tags: {% for tag in highlight.tags -%} #{{tag | replace(" ", "-")+" "}}{%- endfor %}{%- endif %}
{% if highlight.annotation %} - Annotation: {{highlight.annotation}}{%- endif -%}{%- endfor -%}
68 changes: 68 additions & 0 deletions src/modals/manageGroupsModal.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<script lang="ts">
import Select from 'svelte-select';
import type { Group } from '~/models';
export let groups: Group[];
export let onSubmit: (value) => void;
let selectedGroups = groups.filter((group) => group.selected);
let disabledButton = true;
let value;
const optionIdentifier = 'id';
const labelIdentifier = 'name';
function handleSelect(event) {
disabledButton = !selectedGroups;
}
function handleClear(event) {
disabledButton = !selectedGroups;
}
</script>

<div class="setting-item">
<div class="setting-item-info">
<div class="setting-item-description">
<div class="ow-dropdown ow-themed">
<p>
<Select
bind:value={selectedGroups}
isMulti={true}
{optionIdentifier}
{labelIdentifier}
items={groups}
on:select={handleSelect}
on:clear={handleClear}
/>
</p>
</div>
</div>
</div>
</div>

<div class="setting-item ow-info">
<div class="setting-item-info">
<div class="setting-item-description">Note: Use with caution. <br />Highlight(s) and annotation(s) will not sync retroactively.</div>
</div>
<div class="setting-item-control">
<button class="mod-cta" disabled={disabledButton} style="float: right" on:click={() => onSubmit({ selectedGroups })}>Save</button>
</div>
</div>

<style>
.ow-info {
padding-top: 0px;
border-top: 0px !important;
}
.ow-dropdown {
width: 500px !important;
}
.ow-themed {
--border: 3px solid var(--background-modifier-border) !important;
--borderRadius: 3px;
--placeholderColor: var(--text-muted) !important;
--background: var(--background-secondary-alt) !important;
--itemColor: var(--text-normal) !important;
--itemHoverBG: var(--interactive-accent-hover) !important;
--itemHoverColor: var(--text-on-accent) !important;
--listBackground: var(--interactive-normal) !important;
}
</style>
Loading

0 comments on commit 56f99da

Please sign in to comment.