Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also .

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also .
Choose a Base Repository
hypothesis/h
robertknight/h
40a/h
AFDudley/h
BigBlueHat/h
BinaryStars/h
CCH543/h
Cinemacloud/h
Ericgood/h
FTG-003/h
Forethinker/h
GratefulTony/h
HGldJ1966/h
JJediny/h
John-Williams/h
Laurian/h
LittleFancy/h
MattyQ/h
Mishkin2015/h
RichardLitt/h
Staffan1/h
SteelWagstaff/h
TowerBR/h
VanyTang/h
abigailricarte/h
ackermann/h
alecchap/h
alesarrett/h
alexsegura/h
almereyda/h
alon/h
andzi/h
angelicxsoul/h
ansmoh/h
apurvajalit/h
arjunvasan/h
asdevor/h
bZichett/h
badgettrg/Webmarks
balmas/h
balupton/h
bbarker/h
bennlich/h
benthor/h
blakewest/h
bogste/h
bradparks/h
brittanystoroz/h
buiquangchien/h
cdchapman/h
charblanc/h
chowsamihq/h
chr7stos/Webmarks
chrber/h
chrismPssina/h
christinaphamAD/h
cmbirk/h
codeaudit/h
coolcool21/h
cove/h
csillag/h
danjimilk/h
dannyhope/h
daredream/h
davidmcclure/h
dennisplucinik/h
dezynetechnologies/h
diegodlh/h
djcun95/h
donsequitur/h
edsu/h
eiro10/h
emckean/h
ercchy/h
eshellman/h
fangang123/h
fchasen/h
fcrimins/h
fhirsch/h
ficolo/h
fragkopoulos/h
gauravkeerthi/h
geass/h
gergely-ujvari/h
gitter-badger/h
gnott/h
gobengo/h
gorinovic/h
gus3000/h
hashin/h
helemaalbigt/h
hmstepanek/h
hwasiti/h
hylhero/h
hyperstudio/h
iHDeveloper/h
imeysam/h
jackspaceBerkeley/h
jarey/h
jasdeep/h
jason790/h
jasonzou/j
jazahn/h
jccr/h
jean/h
jeka57/h
jeremydean/h
jermnelson/h
jibe-b/h
jnishiyama/h
jojksd/h
jpadilla/h
jtremback/h
judell/h
juli-so/h
kabacs/h
karissa/h
kaushikvijay/h
kaydoh/h
kill4uk/h
klopiinas/h
klrkdekira/h
koulihong311/h
krassif/h
krstnkngs/h
leoqmp/h
linhua55/h
lucadealfaro/h
lyspooner/h
lyzadanger/h
m1yag1/h
magee/h
mambocab/h
manunymous/h
maraino/h
mari-ja/h
markbarratt/h
martinq/h
mbbaig/h
mcarv63/h
meawoppl/h
meflyup/h
metasj/h
mgasner/h
mgax/h
mollycr/h
mrchrisadams/h
mrienstra/h
mshavlovsky/h
muddasani/h
nagyist/hyphothesis-h
nagyistoce/hypothesis-h
nanxio/h
neozhangthe1/h
ningyifan/h
nkingsley/h
nlholdem/h
nlisgo/h
noscripter/h
nshkuro/h
odnodn/h
oliversauter/h
openbizgit/h
opengovfoundation/h
openstax/hypothesis-server
ouroboros8/h
pablomarti/h
pamo/h
philipn/h
philschatz/h
pinballwonder/h
plainspace/h
raowl/h
rickyhan/h
rmoorman/h
rmtsukuru/h
rowhit/h
rsarxiv/h
saakaifoundry/h
samrose/h
scharf/h
shepazu/h
sherah/h
shofheinz/h
soapdog/h
ssin122/test-h
st-fresh/h
stuk88/h
sylvanmist/h
tetratorus/h
tilgovi/h
tomnar/h
trivenews/h
truthadjustr/h
utngz/h
voidfiles/h
wenchen/h
yargevad/h
yumatch/h
zshen777/h
Nothing to show
Choose a base branch
2263-tooltip-for-note-button
anchoring-rewrite
angular-1.3
angular-1.4_rob
app-cors
app_startup_metrics
assets-view-tests
atom-link-related
autoprefix_css
avoid-group-reload-on-change
better-dockerfile-caching
bridge_timeout_debugging
browserify_extension
build_deps_before_test
categorized_logging
change-username-form
check-asset-versions-v2
decaf
deduplicate-password-validation
deps-auto-update
ejp
embedding-docs-link
enable-group-creator-deletion-split-delete-method
enriched-stream
extension-badge-tint
extension_badge_refactor
extension_build_type_indicator
extension_embed_conflict
form-preact
frontend_build_refactor
frontend_tests_docs
gh2505-autolink
gh2553-fix_selection_filter
gh2561-highlight_text_revert
gh2563-test_timeout_debugging
gh2568-safari_missing_urls
gh2641-no_reload_on_groups_list_change
gh2642-reload_fouc
gh2646-bucket_bar_pos
gh2654-safari_search_expander
gh2663-do_not_switch_group_on_leave
groups_changed_exception
groups_list_selection_fix
groups
icon_font_fix
icon_font_fix_2
improve-form-field-list-design
index-and-search-elasticsearch6
index-authority
karma-watch-target
login-with-google
master
multitarget
nipsa
oauth-admin
oauth-login-prototype
p-frontend_build_refactor
p-fx-webextensions
p-new_highlighter
p-new_threading_impl
p-preact_annotation
p-preact_components
p-thread_model
parallel-reindex
publish-btn-cleanup
py3-docker
quote-anchor-integration-test
refactor-sidebar-injector-test
replace-angular_websocket
reply-count-badge
rob-setup_docs_corrections
search-bar-preact
search-bar-tidy-up
search-decaf
search-tests
server_sorted_groups
support-sqs
t87-editorconfig-sass
t87-group_scope_dropdown_ui
t88-remove_self_from_group
t89-combined_scope_save_btn
t89-group_list_css_refactor
t90-search_icon_click
t90-top_bar_new_design
t90-top_bar_refactor
t91-sort_dropdown_move_to_top_bar
t91-sort_dropdown_refactor
t93-clear_selection_btn_ux
t93-create_group_refresh
t105-group_push_notifications
t112-center_post_dropdown
t125-post_button_ui_fixes
t139-new_loading_indicator
t148-card_group_style_refactor
t152-move_unsaved_annot_to_current_group
t182-chrome_perms_refactor
t187-new_homepage_design
thread-collapsing
travis-flake8
v0.2.x
v0.3.x
visual-truncation
websocket-send-on-reconnect
z-login-with-google
z-py3-working-docker-img
z-py3
z-search-tests
Nothing to show
Choose a Head Repository
hypothesis/h
robertknight/h
40a/h
AFDudley/h
BigBlueHat/h
BinaryStars/h
CCH543/h
Cinemacloud/h
Ericgood/h
FTG-003/h
Forethinker/h
GratefulTony/h
HGldJ1966/h
JJediny/h
John-Williams/h
Laurian/h
LittleFancy/h
MattyQ/h
Mishkin2015/h
RichardLitt/h
Staffan1/h
SteelWagstaff/h
TowerBR/h
VanyTang/h
abigailricarte/h
ackermann/h
alecchap/h
alesarrett/h
alexsegura/h
almereyda/h
alon/h
andzi/h
angelicxsoul/h
ansmoh/h
apurvajalit/h
arjunvasan/h
asdevor/h
bZichett/h
badgettrg/Webmarks
balmas/h
balupton/h
bbarker/h
bennlich/h
benthor/h
blakewest/h
bogste/h
bradparks/h
brittanystoroz/h
buiquangchien/h
cdchapman/h
charblanc/h
chowsamihq/h
chr7stos/Webmarks
chrber/h
chrismPssina/h
christinaphamAD/h
cmbirk/h
codeaudit/h
coolcool21/h
cove/h
csillag/h
danjimilk/h
dannyhope/h
daredream/h
davidmcclure/h
dennisplucinik/h
dezynetechnologies/h
diegodlh/h
djcun95/h
donsequitur/h
edsu/h
eiro10/h
emckean/h
ercchy/h
eshellman/h
fangang123/h
fchasen/h
fcrimins/h
fhirsch/h
ficolo/h
fragkopoulos/h
gauravkeerthi/h
geass/h
gergely-ujvari/h
gitter-badger/h
gnott/h
gobengo/h
gorinovic/h
gus3000/h
hashin/h
helemaalbigt/h
hmstepanek/h
hwasiti/h
hylhero/h
hyperstudio/h
iHDeveloper/h
imeysam/h
jackspaceBerkeley/h
jarey/h
jasdeep/h
jason790/h
jasonzou/j
jazahn/h
jccr/h
jean/h
jeka57/h
jeremydean/h
jermnelson/h
jibe-b/h
jnishiyama/h
jojksd/h
jpadilla/h
jtremback/h
judell/h
juli-so/h
kabacs/h
karissa/h
kaushikvijay/h
kaydoh/h
kill4uk/h
klopiinas/h
klrkdekira/h
koulihong311/h
krassif/h
krstnkngs/h
leoqmp/h
linhua55/h
lucadealfaro/h
lyspooner/h
lyzadanger/h
m1yag1/h
magee/h
mambocab/h
manunymous/h
maraino/h
mari-ja/h
markbarratt/h
martinq/h
mbbaig/h
mcarv63/h
meawoppl/h
meflyup/h
metasj/h
mgasner/h
mgax/h
mollycr/h
mrchrisadams/h
mrienstra/h
mshavlovsky/h
muddasani/h
nagyist/hyphothesis-h
nagyistoce/hypothesis-h
nanxio/h
neozhangthe1/h
ningyifan/h
nkingsley/h
nlholdem/h
nlisgo/h
noscripter/h
nshkuro/h
odnodn/h
oliversauter/h
openbizgit/h
opengovfoundation/h
openstax/hypothesis-server
ouroboros8/h
pablomarti/h
pamo/h
philipn/h
philschatz/h
pinballwonder/h
plainspace/h
raowl/h
rickyhan/h
rmoorman/h
rmtsukuru/h
rowhit/h
rsarxiv/h
saakaifoundry/h
samrose/h
scharf/h
shepazu/h
sherah/h
shofheinz/h
soapdog/h
ssin122/test-h
st-fresh/h
stuk88/h
sylvanmist/h
tetratorus/h
tilgovi/h
tomnar/h
trivenews/h
truthadjustr/h
utngz/h
voidfiles/h
wenchen/h
yargevad/h
yumatch/h
zshen777/h
Nothing to show
Choose a head branch
2263-tooltip-for-note-button
anchoring-rewrite
angular-1.3
angular-1.4_rob
app-cors
app_startup_metrics
assets-view-tests
atom-link-related
autoprefix_css
avoid-group-reload-on-change
better-dockerfile-caching
bridge_timeout_debugging
browserify_extension
build_deps_before_test
categorized_logging
change-username-form
check-asset-versions-v2
decaf
deduplicate-password-validation
deps-auto-update
ejp
embedding-docs-link
enable-group-creator-deletion-split-delete-method
enriched-stream
extension-badge-tint
extension_badge_refactor
extension_build_type_indicator
extension_embed_conflict
form-preact
frontend_build_refactor
frontend_tests_docs
gh2505-autolink
gh2553-fix_selection_filter
gh2561-highlight_text_revert
gh2563-test_timeout_debugging
gh2568-safari_missing_urls
gh2641-no_reload_on_groups_list_change
gh2642-reload_fouc
gh2646-bucket_bar_pos
gh2654-safari_search_expander
gh2663-do_not_switch_group_on_leave
groups_changed_exception
groups_list_selection_fix
groups
icon_font_fix
icon_font_fix_2
improve-form-field-list-design
index-and-search-elasticsearch6
index-authority
karma-watch-target
login-with-google
master
multitarget
nipsa
oauth-admin
oauth-login-prototype
p-frontend_build_refactor
p-fx-webextensions
p-new_highlighter
p-new_threading_impl
p-preact_annotation
p-preact_components
p-thread_model
parallel-reindex
publish-btn-cleanup
py3-docker
quote-anchor-integration-test
refactor-sidebar-injector-test
replace-angular_websocket
reply-count-badge
rob-setup_docs_corrections
search-bar-preact
search-bar-tidy-up
search-decaf
search-tests
server_sorted_groups
support-sqs
t87-editorconfig-sass
t87-group_scope_dropdown_ui
t88-remove_self_from_group
t89-combined_scope_save_btn
t89-group_list_css_refactor
t90-search_icon_click
t90-top_bar_new_design
t90-top_bar_refactor
t91-sort_dropdown_move_to_top_bar
t91-sort_dropdown_refactor
t93-clear_selection_btn_ux
t93-create_group_refresh
t105-group_push_notifications
t112-center_post_dropdown
t125-post_button_ui_fixes
t139-new_loading_indicator
t148-card_group_style_refactor
t152-move_unsaved_annot_to_current_group
t182-chrome_perms_refactor
t187-new_homepage_design
thread-collapsing
travis-flake8
v0.2.x
v0.3.x
visual-truncation
websocket-send-on-reconnect
z-login-with-google
z-py3-working-docker-img
z-py3
z-search-tests
Nothing to show
  • 6 commits
  • 8 files changed
  • 0 commit comments
  • 2 contributors
Commits on Oct 29, 2015
Fix exception when GROUPS_CHANGED event is broadcast during route load
The <group-list> directive attempted to update itself in
response to a group change notification without triggering
a full digest cycle by using `$scope.$apply`.

This was based on the incorrect understanding that $apply only
dirty-checks the current scope downwards. In fact, in dirty-checks
the root scope. Additionally, the logic was pointless since
group list/focus changes happen in response to two types of events,
both of which are triggered in the context of $apply:

 * An event handler when the user selects a group

 * A callback from angular-websocket when a WebSocket message
   is received.
Merge pull request #2688 from robertknight/groups_changed_exception
Fix exception when GROUPS_CHANGED event is broadcast during route load
Avoid reloading whole view when focused group changes
When the focused group changes, instead of reloading the whole
view just unload the currently displayed annotations and then
reload the annotations for the newly selected group(s) and
any unsaved drafts.

The user-facing benefit of this is that changes to
unsaved annotations (tags, text etc.) are preserved
when switching groups.
Add some comments to explain what the `threading` service is for.
Add documentation explaining what the threading service
does and links explaining the 'jwz' message threading
algorithm and a reference to the JS implementation that
we are using.
@@ -48,7 +48,7 @@ module.exports = class AppController
# Reload the view when the focused group changes or the
# list of groups that the user is a member of changes
reloadEvents = [events.USER_CHANGED, events.GROUP_FOCUSED];
reloadEvents = [events.USER_CHANGED];
reloadEvents.forEach((eventName) ->
$scope.$on(eventName, (event, data) ->
if !data || !data.initialLoad
@@ -16,15 +16,13 @@ resolve =
'annotationMapper', 'drafts', 'threading'
(annotationMapper, drafts, threading) ->
# Unload all the annotations
idTable = threading.idTable
annotations = (message for id, {message} of idTable when message)
annotationMapper.unloadAnnotations(annotations)
annotationMapper.unloadAnnotations(threading.annotationList())
# Reset the threading root
threading.createIdTable([])
threading.root = mail.messageContainer()
# Thread all the drafts
# Reload all unsaved annotations
threading.thread(drafts.all())
return threading
@@ -1,5 +1,7 @@
### global -validate ###
events = require('../events');
STORAGE_KEY = 'hypothesis.privacy'
# Validate an annotation.
@@ -51,6 +53,7 @@ AnnotationController = [
drafts, flash, permissions, tags, time,
annotationUI, annotationMapper, session, groups, localStorage) ->
# @annotation is the view model, containing the unsaved annotation changes
@annotation = {}
@action = 'view'
@document = null
@@ -59,6 +62,8 @@ AnnotationController = [
@isSidebar = false
@timestamp = null
# 'model' is the domain model, containing the currently saved version
# of the annotation
model = $scope.annotationGet()
# Set the group of new annotations.
@@ -193,7 +198,8 @@ AnnotationController = [
# @description Switches the view to an editor.
###
this.edit = ->
drafts.add model, => this.revert()
if !drafts.get(model)
updateDraft(model)
@action = if model.id? then 'edit' else 'create'
@editing = true
@preview = 'no'
@@ -228,6 +234,14 @@ AnnotationController = [
domainModel, viewModel,
{tags: (tag.text for tag in viewModel.tags)})
updateDraft = (draft) ->
# drafts only preserve the text and tags for the
# annotation, changes to other properties are not preserved
drafts.update(model, {
text: draft.text
tags: draft.tags
})
###*
# @ngdoc method
# @name annotation.AnnotationController#save
@@ -299,7 +313,11 @@ AnnotationController = [
this.render = ->
# Extend the view model with a copy of the domain model.
# Note that copy is used so that deep properties aren't shared.
@annotation = angular.extend {}, angular.copy model
@annotation = angular.extend {}, angular.copy(model)
draft = drafts.get(model)
if draft
angular.extend @annotation, angular.copy(draft)
# Set the URI
@annotationURI = new URL("/a/#{@annotation.id}", this.baseURI).href
@@ -333,7 +351,7 @@ AnnotationController = [
@document.title = @document.title[0..29] + ''
# Form the tags for ngTagsInput.
@annotation.tags = ({text} for text in (model.tags or []))
@annotation.tags = ({text} for text in (@annotation.tags or []))
updateTimestamp = (repeat=false) =>
@timestamp = time.toFuzzyString model.updated
@@ -348,11 +366,6 @@ AnnotationController = [
# Export the baseURI for the share link
this.baseURI = $document.prop('baseURI')
# Discard the draft if the scope goes away.
$scope.$on '$destroy', ->
updateTimestamp = angular.noop
drafts.remove model
# watch for changes to the domain model and recreate the view model
# when it changes
# XXX: TODO: don't clobber the view when collaborating
@@ -369,7 +382,7 @@ AnnotationController = [
$rootScope.$emit('annotationCreated', model)
highlight = false # Prevents double highlight creation.
else
drafts.add model, => this.revert()
updateDraft(model)
updateTimestamp(model is old) # repeat on first run
this.render()
@@ -382,7 +395,22 @@ AnnotationController = [
model.user ?= userid
# Start editing brand new annotations immediately
unless model.id? or (this.isHighlight() and highlight) then this.edit()
isNewAnnotation = !(model.id || (this.isHighlight() && highlight));
if isNewAnnotation || drafts.get(model)
this.edit()
# when the current group changes, if this an unsaved annotation then
# move it to the current group
$scope.$on events.GROUP_FOCUSED, ->
if !vm.editing
return
draftDomainModel = {}
updateDomainModel(draftDomainModel, vm.annotation)
updateDraft(draftDomainModel)
if !model.id
model.group = groups.focused().id
this
]
@@ -32,9 +32,6 @@ function GroupListController($scope, $window, groups) {
$scope.focusGroup = function (groupId) {
groups.focus(groupId);
}
$scope.$on(events.GROUPS_CHANGED, $scope.$apply.bind($scope));
$scope.$on(events.GROUP_FOCUSED, $scope.$apply.bind($scope));
}
/**
@@ -165,20 +165,4 @@ describe('groupList', function () {
clickLeaveIcon(element, true);
assert.notCalled(fakeGroups.focus);
});
it('should update when a GROUPS_CHANGED event is received', function () {
var element = createGroupList();
var groupItems = element.find('.group-item');
assert.equal(groupItems.length, groups.length + 1);
groups.push({
id: 'new-group',
name: 'New Group',
url: GROUP_LINK
});
$rootScope.$broadcast(events.GROUPS_CHANGED);
element.scope.$digest();
groupItems = element.find('.group-item');
assert.equal(groupItems.length, groups.length + 1);
});
});

This file was deleted.

Oops, something went wrong.
@@ -1,7 +1,20 @@
angular = require('angular')
mail = require('./vendor/jwz')
# The threading service provides the model for the currently loaded
# set of annotations, structured as a tree of annotations and replies.
#
# The service listens for events when annotations are loaded, unloaded,
# created or deleted and updates the tree model in response.
#
# The conversion of a flat list of incoming messages into a tree structure
# with replies nested under their parents
# uses an implementation of the `jwz` message threading algorithm
# (see https://www.jwz.org/doc/threading.html and the JS port
# at https://github.com/maxogden/conversationThreading-js).
#
# The 'Threading' service "inherits" from 'mail.messageThread'
#
module.exports = class Threading
root: null
@@ -61,6 +74,11 @@ module.exports = class Threading
this.pruneEmpties(@root)
@root
# Returns a flat list of every annotation that is currently loaded
# in the thread
annotationList: ->
(message for id, {message} of @idTable when message)
pruneEmpties: (parent) ->
for container in parent.children
this.pruneEmpties(container)
@@ -1,13 +1,14 @@
angular = require('angular')
events = require('./events')
module.exports = class WidgetController
this.$inject = [
'$scope', 'annotationUI', 'crossframe', 'annotationMapper', 'groups',
'$scope', 'annotationUI', 'crossframe', 'annotationMapper', 'drafts', 'groups',
'streamer', 'streamFilter', 'store', 'threading'
]
constructor: (
$scope, annotationUI, crossframe, annotationMapper, groups,
$scope, annotationUI, crossframe, annotationMapper, drafts, groups,
streamer, streamFilter, store, threading
) ->
$scope.isStream = true
@@ -17,6 +18,12 @@ module.exports = class WidgetController
@chunkSize = 200
loaded = []
_resetAnnotations = ->
# Unload all the annotations
annotationMapper.unloadAnnotations(threading.annotationList())
# Reload all the drafts
threading.thread(drafts.all())
_loadAnnotationsFrom = (query, offset) =>
queryCore =
limit: @chunkSize
@@ -45,6 +52,11 @@ module.exports = class WidgetController
streamFilter.resetFilter().addClause('/uri', 'one_of', loaded)
streamer.send({filter: streamFilter.getFilter()})
$scope.$on events.GROUP_FOCUSED, ->
_resetAnnotations(annotationMapper, drafts, threading)
loaded = []
loadAnnotations crossframe.frames
$scope.$watchCollection (-> crossframe.frames), loadAnnotations
$scope.focus = (annotation) ->

No commit comments for this range