Brion VIBBER has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/116653

Change subject: Work in progress: save scroll position by element instead of 
raw offset
......................................................................

Work in progress: save scroll position by element instead of raw offset

Instead of saving scroll position as a Y offset in pixels, save a
CSS-style query selector against the nearest section header or content
section chunk and numbered subparagraph, with an offset against that.

Comes closer to keeping us on the 'same' position on rotation, and
should survive various minor editing scenarios.

Not quite perfect right now; as with the previous fragment support we'll
show the loaded HTML before we actually scroll to it. Need to be smarter
about the 'loading' crossfade state, so we stay in the loading indicator
until the desired scroll position is visible and reached.

Another smarter thing would be to scroll as soon as the position exists,
then keep adding subsequent sections, rather than wait for all HTML to
be applied before scrolling.

Bug: 61512
Change-Id: Ia08941adbdd09f5c21eda03deb26e40ac26be792
---
M wikipedia/assets/bundle-test.js
M wikipedia/assets/bundle.js
M wikipedia/src/main/java/org/wikipedia/page/PageViewFragment.java
M www/js/main.js
M www/js/sections.js
5 files changed, 254 insertions(+), 13 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/apps/android/wikipedia 
refs/changes/53/116653/1

diff --git a/wikipedia/assets/bundle-test.js b/wikipedia/assets/bundle-test.js
index 470914c..678d135 100644
--- a/wikipedia/assets/bundle-test.js
+++ b/wikipedia/assets/bundle-test.js
@@ -55,6 +55,78 @@
     bridge.sendMessage( "imagesListResponse", { "images": imageURLs });
 } );
 
+bridge.registerListener( 'scrollToSelector', function( payload ) {
+    var target = document.querySelector( payload.selector );
+    if ( target ) {
+        var top = target.offsetTop + payload.offset;
+        window.scrollTo( 0, top );
+    } else {
+        console.log('no exist');
+    }
+});
+
+function getScrollPositionByElement() {
+    // The system tells us where we are in pixels
+    var top = window.scrollY;
+
+    // But if we rotate, or the page is edited, that'll be useless.
+    // Find the top-level paragraph-ish element within the current section 
that we're in at the top...
+
+    // Could consider using document.elementFromPoint but this may be weird.
+    var content = document.getElementById('content');
+    var el = null;
+    var section = null;
+    for ( el = content.firstChild; el.nextSibling !== null; el = 
el.nextSibling ) {
+        if ( el.offsetTop >= top ) {
+            // We passed it
+            break;
+        } else {
+            section = el;
+        }
+    }
+    if (section === null ) {
+        return {
+            selector: "html",
+            offset: top
+        };
+    }
+
+    // Now look within the section
+    var target = null, n = 0;
+    for ( el = section.firstChild; el.nextSibling !== null; el = 
el.nextSibling ) {
+        if (el.nodeType === Node.ELEMENT_NODE ) {
+            if ( el.offsetTop >= top ) {
+                // We passed it
+                break;
+            } else {
+                target = el;
+            }
+            n++;
+        }
+    }
+
+    if ( target ) {
+        return {
+            selector: "#" + section.id + " > *:nth-child(" + n + ")",
+            offset: top - target.offsetTop
+        };
+    } else {
+        return {
+            selector: '#' + section.id,
+            offset: top - section.offsetTop
+        };
+    }
+}
+
+function updateScrollPosition() {
+    var positionData = getScrollPositionByElement();
+    bridge.sendMessage( "updateScrollPosition", positionData );
+}
+
+window.addEventListener('scroll', function() {
+    updateScrollPosition();
+});
+
 },{"./bridge":1}],3:[function(require,module,exports){
 var bridge = require("../js/bridge");
 bridge.registerListener( "injectScript", function( payload ) {
diff --git a/wikipedia/assets/bundle.js b/wikipedia/assets/bundle.js
index 3ddbe0f..0ba78b1 100644
--- a/wikipedia/assets/bundle.js
+++ b/wikipedia/assets/bundle.js
@@ -103,6 +103,78 @@
     bridge.sendMessage( "imagesListResponse", { "images": imageURLs });
 } );
 
+bridge.registerListener( 'scrollToSelector', function( payload ) {
+    var target = document.querySelector( payload.selector );
+    if ( target ) {
+        var top = target.offsetTop + payload.offset;
+        window.scrollTo( 0, top );
+    } else {
+        console.log('no exist');
+    }
+});
+
+function getScrollPositionByElement() {
+    // The system tells us where we are in pixels
+    var top = window.scrollY;
+
+    // But if we rotate, or the page is edited, that'll be useless.
+    // Find the top-level paragraph-ish element within the current section 
that we're in at the top...
+
+    // Could consider using document.elementFromPoint but this may be weird.
+    var content = document.getElementById('content');
+    var el = null;
+    var section = null;
+    for ( el = content.firstChild; el.nextSibling !== null; el = 
el.nextSibling ) {
+        if ( el.offsetTop >= top ) {
+            // We passed it
+            break;
+        } else {
+            section = el;
+        }
+    }
+    if (section === null ) {
+        return {
+            selector: "html",
+            offset: top
+        };
+    }
+
+    // Now look within the section
+    var target = null, n = 0;
+    for ( el = section.firstChild; el.nextSibling !== null; el = 
el.nextSibling ) {
+        if (el.nodeType === Node.ELEMENT_NODE ) {
+            if ( el.offsetTop >= top ) {
+                // We passed it
+                break;
+            } else {
+                target = el;
+            }
+            n++;
+        }
+    }
+
+    if ( target ) {
+        return {
+            selector: "#" + section.id + " > *:nth-child(" + n + ")",
+            offset: top - target.offsetTop
+        };
+    } else {
+        return {
+            selector: '#' + section.id,
+            offset: top - section.offsetTop
+        };
+    }
+}
+
+function updateScrollPosition() {
+    var positionData = getScrollPositionByElement();
+    bridge.sendMessage( "updateScrollPosition", positionData );
+}
+
+window.addEventListener('scroll', function() {
+    updateScrollPosition();
+});
+
 },{"./bridge":2}],5:[function(require,module,exports){
 var bridge = require("./bridge");
 
@@ -124,7 +196,7 @@
 
     var content = document.createElement( "div" );
     content.innerHTML = payload.section.text;
-    content.id = "#content_block_0";
+    content.id = "content_block_0";
     content = transformer.transform( "leadSection", content );
     content = transformer.transform( "section", content );
     document.getElementById( "content" ).appendChild( content );
@@ -162,8 +234,12 @@
         bridge.sendMessage( "requestSection", { index: payload.index + 1 } );
     } else {
         document.getElementById( "loading_sections").className = "";
-        if ( typeof payload.fragment === "string" ) {
-            scrollToSection( payload.fragment );
+        if ( typeof payload.scrollSelector === "string" ) {
+            var target = document.querySelector( payload.scrollSelector );
+            if ( target ) {
+                var top = target.offsetTop + payload.scrollOffset;
+                window.scrollTo( 0, top );
+            }
         }
     }
 });
diff --git a/wikipedia/src/main/java/org/wikipedia/page/PageViewFragment.java 
b/wikipedia/src/main/java/org/wikipedia/page/PageViewFragment.java
index c8da5b7..ce3e274 100644
--- a/wikipedia/src/main/java/org/wikipedia/page/PageViewFragment.java
+++ b/wikipedia/src/main/java/org/wikipedia/page/PageViewFragment.java
@@ -21,7 +21,8 @@
     private static final String KEY_TITLE = "title";
     private static final String KEY_PAGE = "page";
     private static final String KEY_STATE = "state";
-    private static final String KEY_SCROLL_Y = "scrollY";
+    private static final String KEY_SCROLL_SELECTOR = "scrollSelector";
+    private static final String KEY_SCROLL_OFFSET = "scrollOffset";
     private static final String KEY_CURRENT_HISTORY_ENTRY = 
"currentHistoryEntry";
     private static final String KEY_QUICK_RETURN_BAR_ID = "quickReturnBarId";
 
@@ -49,7 +50,8 @@
     private WikipediaApp app;
     private Api api;
 
-    private int scrollY;
+    private String scrollSelector;
+    private int scrollOffset;
     private int quickReturnBarId;
 
     private View quickReturnBar;
@@ -105,7 +107,8 @@
         outState.putParcelable(KEY_TITLE, title);
         outState.putParcelable(KEY_PAGE, page);
         outState.putInt(KEY_STATE, state);
-        outState.putInt(KEY_SCROLL_Y, webView.getScrollY());
+        outState.putString(KEY_SCROLL_SELECTOR, scrollSelector);
+        outState.putInt(KEY_SCROLL_OFFSET, scrollOffset);
         outState.putParcelable(KEY_CURRENT_HISTORY_ENTRY, curEntry);
         outState.putInt(KEY_QUICK_RETURN_BAR_ID, quickReturnBarId);
     }
@@ -124,7 +127,8 @@
                 page = savedInstanceState.getParcelable(KEY_PAGE);
             }
             state = savedInstanceState.getInt(KEY_STATE);
-            scrollY = savedInstanceState.getInt(KEY_SCROLL_Y);
+            scrollSelector = savedInstanceState.getString(KEY_SCROLL_SELECTOR);
+            scrollOffset = savedInstanceState.getInt(KEY_SCROLL_OFFSET);
             curEntry = 
savedInstanceState.getParcelable(KEY_CURRENT_HISTORY_ENTRY);
             quickReturnBarId = 
savedInstanceState.getInt(KEY_QUICK_RETURN_BAR_ID);
         }
@@ -145,7 +149,6 @@
 
         bridge = new CommunicationBridge(webView, 
"file:///android_asset/index.html");
         setupMessageHandlers();
-        Utils.addUtilityMethodsToBridge(getActivity(), bridge);
         Utils.setupDirectionality(title.getSite().getLanguage(), bridge);
         linkHandler = new LinkHandler(getActivity(), bridge, title.getSite());
         app = (WikipediaApp)getActivity().getApplicationContext();
@@ -180,12 +183,27 @@
                     wrapper.put("section", 
page.getSections().get(index).toJSON());
                     wrapper.put("index", index);
                     wrapper.put("isLast", index == page.getSections().size() - 
1);
-                    wrapper.put("fragment", page.getTitle().getFragment());
+                    if (scrollSelector != null) {
+                        wrapper.put("scrollSelector", scrollSelector);
+                        wrapper.put("scrollOffset", scrollOffset);
+                    } else if (page.getTitle().getFragment() != null) {
+                        wrapper.put("scrollSelector", "#" + 
page.getTitle().getFragment()); // check for encoding issues?
+                        wrapper.put("scrollOffset", 0);
+                    }
                     bridge.sendMessage("displaySection", wrapper);
                 } catch (JSONException e) {
                     // Won't happen
                     throw new RuntimeException(e);
                 }
+            }
+        });
+
+        bridge.addListener( "updateScrollPosition", new 
CommunicationBridge.JSEventListener() {
+            @Override
+            public void onMessage(String messageType, JSONObject 
messagePayload) {
+                Log.d("Wikipedia", "updateScrollPosition: " + 
messagePayload.toString());
+                scrollSelector = messagePayload.optString("selector");
+                scrollOffset = messagePayload.optInt("offset");
             }
         });
     }
@@ -211,7 +229,6 @@
             case STATE_COMPLETE_FETCH:
                 displayLeadSection(page);
                 populateNonLeadSections(page);
-                webView.setScrollY(scrollY);
                 break;
         }
     }
diff --git a/www/js/main.js b/www/js/main.js
index ab9ae9c..3b386dd 100644
--- a/www/js/main.js
+++ b/www/js/main.js
@@ -15,3 +15,75 @@
     }
     bridge.sendMessage( "imagesListResponse", { "images": imageURLs });
 } );
+
+bridge.registerListener( 'scrollToSelector', function( payload ) {
+    var target = document.querySelector( payload.selector );
+    if ( target ) {
+        var top = target.offsetTop + payload.offset;
+        window.scrollTo( 0, top );
+    } else {
+        console.log('no exist');
+    }
+});
+
+function getScrollPositionByElement() {
+    // The system tells us where we are in pixels
+    var top = window.scrollY;
+
+    // But if we rotate, or the page is edited, that'll be useless.
+    // Find the top-level paragraph-ish element within the current section 
that we're in at the top...
+
+    // Could consider using document.elementFromPoint but this may be weird.
+    var content = document.getElementById('content');
+    var el = null;
+    var section = null;
+    for ( el = content.firstChild; el.nextSibling !== null; el = 
el.nextSibling ) {
+        if ( el.offsetTop >= top ) {
+            // We passed it
+            break;
+        } else {
+            section = el;
+        }
+    }
+    if (section === null ) {
+        return {
+            selector: "html",
+            offset: top
+        };
+    }
+
+    // Now look within the section
+    var target = null, n = 0;
+    for ( el = section.firstChild; el.nextSibling !== null; el = 
el.nextSibling ) {
+        if (el.nodeType === Node.ELEMENT_NODE ) {
+            if ( el.offsetTop >= top ) {
+                // We passed it
+                break;
+            } else {
+                target = el;
+            }
+            n++;
+        }
+    }
+
+    if ( target ) {
+        return {
+            selector: "#" + section.id + " > *:nth-child(" + n + ")",
+            offset: top - target.offsetTop
+        };
+    } else {
+        return {
+            selector: '#' + section.id,
+            offset: top - section.offsetTop
+        };
+    }
+}
+
+function updateScrollPosition() {
+    var positionData = getScrollPositionByElement();
+    bridge.sendMessage( "updateScrollPosition", positionData );
+}
+
+window.addEventListener('scroll', function() {
+    updateScrollPosition();
+});
diff --git a/www/js/sections.js b/www/js/sections.js
index dc48dd3..e4e17b5 100644
--- a/www/js/sections.js
+++ b/www/js/sections.js
@@ -12,7 +12,7 @@
 
     var content = document.createElement( "div" );
     content.innerHTML = payload.section.text;
-    content.id = "#content_block_0";
+    content.id = "content_block_0";
     content = transformer.transform( "leadSection", content );
     content = transformer.transform( "section", content );
     document.getElementById( "content" ).appendChild( content );
@@ -50,8 +50,12 @@
         bridge.sendMessage( "requestSection", { index: payload.index + 1 } );
     } else {
         document.getElementById( "loading_sections").className = "";
-        if ( typeof payload.fragment === "string" ) {
-            scrollToSection( payload.fragment );
+        if ( typeof payload.scrollSelector === "string" ) {
+            var target = document.querySelector( payload.scrollSelector );
+            if ( target ) {
+                var top = target.offsetTop + payload.scrollOffset;
+                window.scrollTo( 0, top );
+            }
         }
     }
 });

-- 
To view, visit https://gerrit.wikimedia.org/r/116653
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ia08941adbdd09f5c21eda03deb26e40ac26be792
Gerrit-PatchSet: 1
Gerrit-Project: apps/android/wikipedia
Gerrit-Branch: master
Gerrit-Owner: Brion VIBBER <br...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to