Skip to content
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

Feature/explorer routing #344

Merged
merged 70 commits into from
Aug 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
d75142d
Add the exploration router as specified in #62
jgonggrijp Jun 10, 2020
aa6598a
Add a global instance of the exploration router (#62, #106)
jgonggrijp Jun 10, 2020
8357687
Merge branch 'develop' into feature/explorer-routing
jgonggrijp Jun 16, 2020
6a1cf3c
Add the minimum unittest for the class picker view (#289)
jgonggrijp Jun 16, 2020
8d1dce2
Simplify OntologyClassPickerItemView a bit
jgonggrijp Jun 16, 2020
06511a9
Turn OntologyClassPickerItemView into a CompositeView (#230)
jgonggrijp Jun 16, 2020
e3b411c
Fix, simplify and CollectionView-ify OntologyClassPicker (#230, #289)
jgonggrijp Jun 16, 2020
905b6c9
Stop passing around a complete ontology everywhere (#289)
jgonggrijp Jun 16, 2020
40a426b
Merge branch 'develop' into feature/explorer-routing
jgonggrijp Jul 2, 2020
bc30de9
Make CategoryColorsView self-rendering, self-updating and cleaner (#289)
jgonggrijp Jul 2, 2020
da47c05
Drop the obsolete mock ontology global (#289)
jgonggrijp Jul 2, 2020
e1c2c69
Allow importing the global ontology directly from other globals (#289)
jgonggrijp Jul 2, 2020
d4550a8
Lift the category styles into a global (#289)
jgonggrijp Jul 2, 2020
0de5964
Clean up the imports in the readit aspect (#289, #106)
jgonggrijp Jul 2, 2020
2695ee2
Lift the sources graph into a global (#289)
jgonggrijp Jul 2, 2020
d1ab673
Quickfix: make source list language subviews live-updating (#287 #304)
jgonggrijp Jul 2, 2020
5836786
Drop dead code and stray imports (#287 #304)
jgonggrijp Jul 2, 2020
aecd977
Lift the source list view into a global (#289)
jgonggrijp Jul 2, 2020
37c8786
Make ExplorerView.prototype.setHeight chainable (#289)
jgonggrijp Jul 2, 2020
4b08e21
Lift the explorer view into a global (#289 #106 #62)
jgonggrijp Jul 2, 2020
b5bead0
Drop a stray import (#106)
jgonggrijp Jul 2, 2020
17f71f5
Flatten (don't nest) the event bindings in the readit aspect (#106)
jgonggrijp Jul 2, 2020
f690393
Group bindings in the readit aspect by emitter (#106)
jgonggrijp Jul 2, 2020
8a91316
Optimize line length in the readit aspect module (#106)
jgonggrijp Jul 2, 2020
d2c2a29
Factor account registration into a separate aspect (#106)
jgonggrijp Jul 2, 2020
5e0a5e1
Merge branch 'develop' into feature/explorer-routing
jgonggrijp Jul 16, 2020
c48ecc8
Merge branch 'develop' into feature/explorer-routing
jgonggrijp Aug 11, 2020
18f61d3
Drop a rudimental ontology option
jgonggrijp Aug 11, 2020
bf55948
Replace global imports in unit modules (argh!) by event bindings
jgonggrijp Aug 11, 2020
ba742a0
Merge obsolete directionFsm into userFsm (#256, #260, #106)
jgonggrijp Aug 11, 2020
9b7ef0e
Move scroll easings from global to core (#290)
jgonggrijp Aug 11, 2020
91323b4
Reinstate backbone-machina
jgonggrijp Aug 11, 2020
987905c
Remove registering state from userFsm (#256, #260)
jgonggrijp Aug 11, 2020
f527a3a
Rename DirectionRouter to MainRouter for clarity (#106)
jgonggrijp Aug 11, 2020
e0dcda6
Rename "readit" aspect to "navigation" for clarity (#106)
jgonggrijp Aug 11, 2020
adbbc2b
Do some more cleanup in the navigation aspect (#106)
jgonggrijp Aug 11, 2020
291e3b0
Rename "panel-explorer" directory to "explorer" (#106)
jgonggrijp Aug 12, 2020
ad8f25d
Remove circular dependency between explorer view and controller (#106)
jgonggrijp Aug 12, 2020
7e7c579
Add routes for the external resources panels (#62)
jgonggrijp Aug 12, 2020
788cb53
Factor out route patterns from the exploration router (#62, #106)
jgonggrijp Aug 12, 2020
ea4d931
Add a utility for self-reporting explorer routes in panels (#62, #106)
jgonggrijp Aug 12, 2020
1ff2ab1
Announce the current route from each panel on scroll (#62, #106)
jgonggrijp Aug 12, 2020
106bd07
Import _.invert in the exploration router module (oops) (#62)
jgonggrijp Aug 13, 2020
4525326
Add exploration aspect, navigate on scroll (#62, #106)
jgonggrijp Aug 13, 2020
11f1853
Don't omit the margin on rightward scroll (#110 #271 #318 #62 #106)
jgonggrijp Aug 13, 2020
5737570
Scroll and navigate on every panel manipulation (#62, #106)
jgonggrijp Aug 13, 2020
8dd593a
Enable explorer scrolling by panel cid (#62, #106)
jgonggrijp Aug 18, 2020
2332bfa
Save the panel cid when navigating (#62, #106)
jgonggrijp Aug 18, 2020
091b28d
Scroll to a previously visited panel on route (#62 #106)
jgonggrijp Aug 18, 2020
42d7afa
Replace agnostic async check by gnostic sync check (#106)
jgonggrijp Aug 18, 2020
baa0a13
Add the option to reset the explorer entirely (#106 #62)
jgonggrijp Aug 18, 2020
0502198
Remove stray import from explorer controller module (#289 #106)
jgonggrijp Aug 18, 2020
7e0c6ee
Refactor the explorer event controller (#106 #289 #342)
jgonggrijp Aug 18, 2020
ee99717
Un-defer reopening the annotation list (#106 #289)
jgonggrijp Aug 18, 2020
4eb8c9c
Move explorer controller event bindings to the aspect module (#106 #289)
jgonggrijp Aug 18, 2020
66e6608
Rename "source" explorer route to "source:bare" (#62)
jgonggrijp Aug 19, 2020
1c8ffba
Add explorer view method for checking panel existence (#62 #106)
jgonggrijp Aug 19, 2020
881d7a0
Attach the explorer when matching an explorer route (#62 #106)
jgonggrijp Aug 19, 2020
2438797
Add explorer event controller methods for reset options (#106)
jgonggrijp Aug 19, 2020
080722a
Add the explorer route parser (#62 #106)
jgonggrijp Aug 19, 2020
b33c8c6
Reset the explorer when starting with a deep link (#106)
jgonggrijp Aug 19, 2020
c4234db
Implement #323
jgonggrijp Aug 19, 2020
d4e3520
Allow hard routes in the announceRoute utility (#106 #62)
jgonggrijp Aug 20, 2020
9c8a144
Announce the /explore route from the source list panel (#62)
jgonggrijp Aug 20, 2020
11baf89
Move ensureSources from the navigation to the exploration aspect (#106)
jgonggrijp Aug 20, 2020
9f12806
Enable scroll/reset on route for the source list panel (#62 #106)
jgonggrijp Aug 20, 2020
636d4eb
Always scrolls panels to the rightmost edge of the viewport (#110 #271)
jgonggrijp Aug 20, 2020
ad0e492
Factor out the scrollOrAction pattern (#106)
jgonggrijp Aug 20, 2020
d30ed09
Replace route parser by a pattern that is easier on the brain (#62 #106)
jgonggrijp Aug 20, 2020
2e8e872
Reorder the exploration aspect module (#106)
jgonggrijp Aug 20, 2020
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
2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"async": "^3.1.0",
"backbone": "^1.4.0",
"backbone-fractal": "^1.1.0",
"backbone-machina": "^1.1.0",
"backbone.radio": "^2.0.0",
"bulma": "^0.7.1",
"bulma-accordion": "^2.0.1",
Expand All @@ -28,7 +29,6 @@
"js-cookie": "^2.2.0",
"jsonld": "^1.5.1",
"lodash": "^4.17.10",
"machina": "^2.0.2",
"rangy": "^1.3.0",
"rdf-parse": "^1.1.1",
"streamify-string": "^1.0.1"
Expand Down
11 changes: 2 additions & 9 deletions frontend/src/annotation/panel-annotation-edit-view-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ describe('AnnotationEditView', function() {
range,
positionDetails: this.positionDetails,
source: new Node({'@id': 'x'}),
ontology: new Graph(),
model: undefined,
})).not.toThrow();
});
Expand All @@ -46,10 +45,7 @@ describe('AnnotationEditView', function() {
const annotation = items.get(item('100'));
const creator = annotation.get(dcterms.creator)[0] as Node;
ldChannel.reply('current-user-uri', constant(creator.id));
const view = new AnnotationEditView({
ontology: new Graph(),
model: annotation,
}).render();
const view = new AnnotationEditView({ model: annotation }).render();
expect(view.$('.panel-footer button.is-danger').length).toBe(1);
view.remove();
ldChannel.stopReplying('current-user-uri');
Expand All @@ -59,10 +55,7 @@ describe('AnnotationEditView', function() {
const items = new Graph(mockItems);
const annotation = items.get(item('100'));
const creator = annotation.get(dcterms.creator)[0] as Node;
const view = new AnnotationEditView({
ontology: new Graph(),
model: annotation,
}).render();
const view = new AnnotationEditView({ model: annotation }).render();
expect(view.$('.panel-footer button.is-danger').length).toBe(0);
view.remove();
});
Expand Down
26 changes: 18 additions & 8 deletions frontend/src/annotation/panel-annotation-edit-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ import Graph from '../jsonld/graph';

import ItemEditor from '../panel-ld-item/ld-item-edit-view';
import PickerView from '../forms/base-picker-view';
import FilteredCollection from '../utilities/filtered-collection';
import ItemGraph from '../utilities/item-graph';
import ClassPickerView from '../utilities/ontology-class-picker/ontology-class-picker-view';
import ItemMetadataView from '../utilities/item-metadata/item-metadata-view';
import SnippetView from '../utilities/snippet-view/snippet-view';
import { isType } from '../utilities/utilities';
import { isRdfsClass, isType } from '../utilities/utilities';
import {
AnnotationPositionDetails,
getTargetDetails
Expand All @@ -22,20 +23,30 @@ import {
composeAnnotation,
getAnonymousTextQuoteSelector
} from './../utilities/annotation/annotation-creation-utilities';
import explorerChannel from '../explorer/radio';
import { announceRoute } from '../explorer/utilities';

import BaseAnnotationView from './base-annotation-view';
import FlatCollection from './flat-annotation-collection';

import annotationEditTemplate from './panel-annotation-edit-template';

/**
* Helper function in order to pass the right classes to the classPicker.
*/
function getOntologyClasses() {
const ontology = ldChannel.request('ontology:graph') || new Graph();
return new FilteredCollection<Node, Graph>(ontology, isRdfsClass);
}

const announce = announceRoute('item:edit', ['model', 'id']);

export interface ViewOptions extends BaseOpt<Model> {
/**
* An instance of oa:Annotation that links to a oa:TextQuoteSelector,
* can be undefined if range and positionDetails are set (i.e. in case of a new annotation)
*/
model: Node;
ontology: Graph;
collection?: FlatCollection;

/**
Expand All @@ -56,7 +67,6 @@ export interface ViewOptions extends BaseOpt<Model> {

export default class AnnotationEditView extends BaseAnnotationView {
collection: FlatCollection;
ontology: Graph;
source: Node;
range: Range;
positionDetails: AnnotationPositionDetails;
Expand All @@ -77,7 +87,6 @@ export default class AnnotationEditView extends BaseAnnotationView {
}

initialize(options: ViewOptions): this {
this.ontology = options.ontology;
this.itemOptions = new ItemGraph();
this.itemPicker = new PickerView({collection: this.itemOptions});
this.itemPicker.on('change', this.selectItem, this);
Expand All @@ -97,6 +106,7 @@ export default class AnnotationEditView extends BaseAnnotationView {
this.listenTo(this, 'textQuoteSelector', this.processTextQuoteSelector);
this.processModel(options.model);
this.listenTo(this.model, 'change', this.processModel);
this.on('announceRoute', announce);
}
else {
this.processTextQuoteSelector(this.model);
Expand Down Expand Up @@ -144,7 +154,7 @@ export default class AnnotationEditView extends BaseAnnotationView {

initClassPicker(): this {
this.classPicker = new ClassPickerView({
collection: this.ontology,
collection: getOntologyClasses(),
preselection: this.preselection
});

Expand Down Expand Up @@ -224,15 +234,15 @@ export default class AnnotationEditView extends BaseAnnotationView {
const anno = results.annotation;
this.collection.underlying.add(anno);
const flat = this.collection.get(anno.id);
this.trigger('annotationEditView:saveNew', this, flat, results.items);
explorerChannel.trigger('annotationEditView:saveNew', this, flat, results.items);
}
);
}

submitOldAnnotation(newItem: boolean): void {
this.selectedItem && this.model.set(oa.hasBody, this.selectedItem);
this.model.save();
this.trigger('annotationEditView:save', this, this.model, newItem);
explorerChannel.trigger('annotationEditView:save', this, this.model, newItem);
}

reset(): this {
Expand Down Expand Up @@ -289,7 +299,7 @@ export default class AnnotationEditView extends BaseAnnotationView {
onCancelClicked(event: JQueryEventObject): this {
event.preventDefault();
this.reset();
this.trigger('annotationEditView:close', this);
explorerChannel.trigger('annotationEditView:close', this);
return this;
}

Expand Down
10 changes: 7 additions & 3 deletions frontend/src/annotation/panel-annotation-list-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@ import { CollectionView } from '../core/view';
import { getScrollTop, animatedScroll, ScrollType } from './../utilities/scrolling-utilities';
import ItemSummaryBlock from '../utilities/item-summary-block/item-summary-block-view';
import LoadingSpinnerView from '../utilities/loading-spinner/loading-spinner-view';
import explorerChannel from '../explorer/radio';
import { announceRoute } from '../explorer/utilities';

import FlatModel from './flat-annotation-model';
import FlatCollection from './flat-annotation-collection';
import annotationsTemplate from './panel-annotation-list-template';

const announce = announceRoute('source:annotated', ['model', 'id']);

/**
* Explorer panel that displays a list of annotations as ItemSummaryBlocks.
*
Expand Down Expand Up @@ -41,7 +45,7 @@ export default class AnnotationListView extends CollectionView<FlatModel, ItemSu
remove: this.removeItem,
sort: this.placeItems,
reset: this.resetItems,
});
}).on('announceRoute', announce);
}

_hideLoadingSpinner(): void {
Expand All @@ -52,12 +56,12 @@ export default class AnnotationListView extends CollectionView<FlatModel, ItemSu

_handleFocus(model: FlatModel): void {
this.scrollTo(model);
this.trigger('annotationList:showAnnotation', this, model);
explorerChannel.trigger('annotationList:showAnnotation', this, model);
}

_handleBlur(lostFocus: FlatModel, gainedFocus?: FlatModel): void {
if (!gainedFocus) {
this.trigger('annotationList:hideAnnotation', this, lostFocus);
explorerChannel.trigger('annotationList:hideAnnotation', this, lostFocus);
}
}

Expand Down
14 changes: 0 additions & 14 deletions frontend/src/aspects/authentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import user from '../global/user';
import userFsm from '../global/user-fsm';
import authFsm from '../global/authentication-fsm';
import loginForm from '../global/login-view';
import registrationForm from '../global/registration-view';
import menuView from '../global/menu-view';


Expand Down Expand Up @@ -52,9 +51,6 @@ user.on('login:error', () => {

});
user.on('logout:success', () => authFsm.handle('logout'));
user.on('registration:success', () => registrationForm.success());
user.on('registration:error', (response) => registrationForm.error(response));
user.on('registration:invalid', (errors) => registrationForm.invalid(errors));

// When authorization fails, just return to the last unprivileged state.
userFsm.on('enter:authorizationDenied', () => {
Expand All @@ -74,21 +70,11 @@ userFsm.on('enter:requestAuthorization', (fsm, action) => {
authFsm.handle('login');
});

userFsm.on('enter:registering', () => {
authFsm.handle('register');
});

authFsm.on('enter:unauthenticated', () => userFsm.handle('denied'));
authFsm.on('enter:attemptLogin', () => loginForm.render().$el.appendTo('body'));
authFsm.on('exit:attemptLogin', () => loginForm.$el.detach());
authFsm.on('enter:authenticated', () => userFsm.handle('granted'));
authFsm.on('exit:authenticated', () => userFsm.handle('logout'));
authFsm.on('enter:registering', () => {
registrationForm.render().$el.appendTo('body');
});
authFsm.on('exit:registering', () => {
registrationForm.$el.detach();
});

loginForm.on('submit', credentials => user.login(credentials));
loginForm.on('cancel', () => authFsm.handle('loginCancel'));
Expand Down
69 changes: 69 additions & 0 deletions frontend/src/aspects/exploration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { partial, isString } from 'lodash';

import channel from '../explorer/radio';
import * as act from '../explorer/route-actions';
import router from '../global/exploration-router';
import mainRouter from '../global/main-router';
import explorer from '../global/explorer-view';
import controller from '../global/explorer-controller';
import { ensureSources } from '../global/sources';
import sourceListPanel from '../global/source-list-view';

const browserHistory = window.history;
const resetSourceList = () => explorer.reset(sourceListPanel);

/**
* Common patterns for the explorer routes.
*/
function deepRoute(obtainAction, resetAction) {
return ([serial]) => explorer.scrollOrAction(
browserHistory.state,
() => resetAction(controller, obtainAction(serial))
);
}
const sourceRoute = partial(deepRoute, act.getSource);
const itemRoute = partial(deepRoute, act.getItem);

mainRouter.on('route:explore', () => {
ensureSources();
explorer.scrollOrAction(sourceListPanel.cid, resetSourceList);
});

router.on('route:source:bare', sourceRoute(act.sourceWithoutAnnotations));
router.on('route:source:annotated', sourceRoute(act.sourceWithAnnotations));
router.on('route:item', itemRoute(act.item));
router.on('route:item:edit', itemRoute(act.itemInEditMode));
router.on('route:item:related', itemRoute(act.itemWithRelations));
router.on('route:item:related:edit', itemRoute(act.itemWithEditRelations));
router.on('route:item:external', itemRoute(act.itemWithExternal));
router.on('route:item:external:edit', itemRoute(act.itemWithEditExternal));
router.on('route:item:annotations', itemRoute(act.itemWithOccurrences));

channel.on({
'sourceview:showAnnotations': controller.reopenSourceAnnotations,
'sourceview:hideAnnotations': controller.unlistSourceAnnotations,
'sourceview:textSelected': controller.selectText,
'annotationList:showAnnotation': controller.openSourceAnnotation,
'annotationList:hideAnnotation': controller.closeSourceAnnotation,
'annotationEditView:saveNew': controller.saveNewAnnotation,
'annotationEditView:save': controller.saveAnnotation,
'annotationEditView:close': controller.closeEditAnnotation,
'lditem:showRelated': controller.listRelated,
'lditem:showAnnotations': controller.listItemAnnotations,
'lditem:showExternal': controller.listExternal,
'lditem:editAnnotation': controller.editAnnotation,
'lditem:editItem': controller.notImplemented,
'relItems:itemClick': controller.openRelated,
'relItems:edit': controller.editRelated,
'externalItems:edit': controller.editExternal,
'externalItems:edit-close': controller.closeEditExternal,
'relItems:edit-close': controller.closeEditRelated,
'source-list:click': controller.pushSourcePair,
'searchResultList:itemClicked': controller.openSearchResult,
}, controller);
channel.on('currentRoute', (route, panel) => {
router.navigate(route);
// Amend the state that Backbone.history just pushed with the cid of the
// panel.
browserHistory.replaceState(panel.cid, document.title);
});
49 changes: 49 additions & 0 deletions frontend/src/aspects/navigation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { history } from 'backbone';

import footerView from '../global/footer-view';
import menuView from '../global/menu-view';
import welcomeView from '../global/welcome-view';
import feedbackView from '../global/feedback-view';
import uploadSourceForm from '../global/upload-source-form';
import categoryStyles from '../global/category-styles';
import user from '../global/user';
import mainRouter from '../global/main-router';
import explorationRouter from '../global/exploration-router';
import userFsm from '../global/user-fsm';
import explorerView from '../global/explorer-view';

history.once('route', () => {
menuView.render().$el.appendTo('#header');
footerView.render().$el.appendTo('.footer');
categoryStyles.$el.appendTo('body');
// 133 is the height of the footer (got this number by manually testing)
// Note that the same number needs to be the height of the 'push' class in
// main.sass. 555 is min-height.
const availableHeight = Math.max($(window).height() - 160, 555);
explorerView.setHeight(availableHeight).render();
uploadSourceForm.setHeight(availableHeight);
});

mainRouter.on('route:home', () => mainRouter.navigate('search'));
mainRouter.on('route:search', () => userFsm.handle('search'));
mainRouter.on('route:upload', () => userFsm.handle('upload'));
mainRouter.on('route:explore', () => userFsm.handle('explore'));
mainRouter.on('route:leave', () => userFsm.handle('leave'));

explorationRouter.on('route', () => userFsm.handle('explore'));

userFsm.on('enter:searching', () => welcomeView.render().$el.appendTo('#main'));
userFsm.on('exit:searching', () => welcomeView.$el.detach());
userFsm.on('enter:uploading', () => {
uploadSourceForm.render().$el.appendTo('#main');
});
userFsm.on('exit:uploading', () => {
uploadSourceForm.reset();
uploadSourceForm.$el.detach();
});
userFsm.on('enter:exploring', () => explorerView.$el.appendTo('#main'));
userFsm.on('exit:exploring', () => explorerView.$el.detach());

menuView.on('feedback', () => feedbackView.render().$el.appendTo('body'));

feedbackView.on('close', () => feedbackView.$el.detach());
Loading