ChangeScreen2: Poll for updates in the background
Poll every 30 seconds for updates made to the currently open change. If a modification is detected show a small butter bar in the bottom right corner advising the user they are viewing a stale version of the change. To be efficient this relies on the recently added support to reply "304 Not Modified" when there are no updates. change.updateDelay can be configured by the site administrator to manage the polling frequency. Change-Id: Ib3f3acf513193bf1f1f92c472cd586999f3cad5d
This commit is contained in:
parent
cd5035d179
commit
b9ebb668f6
@ -705,6 +705,28 @@ link:cmd-flush-caches.html[gerrit flush-caches].
|
||||
+
|
||||
Default is 5 minutes.
|
||||
|
||||
[[change]]Section change
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
[[change.updateDelay]]change.updateDelay::
|
||||
+
|
||||
How often in seconds the web interface should poll for updates to the
|
||||
currently open change. The poller relies on the client's browser
|
||||
cache to use If-Modified-Since and respect `304 Not Modified` HTTP
|
||||
reponses. This allows for fast polls, often under 8 milliseconds.
|
||||
+
|
||||
With a configured 30 second delay a server with 4900 active users will
|
||||
typically need to dedicate 1 CPU to the update check. 4900 users
|
||||
divided by an average delay of 30 seconds is 163 requests arriving per
|
||||
second. If requests are served at ~6 ms response time, 1 CPU is
|
||||
necessary to keep up with the update request traffic. On a smaller
|
||||
user base of 500 active users, the default 30 second delay is only 17
|
||||
requests per second and requires ~10% CPU.
|
||||
+
|
||||
If 0 the update polling is disabled.
|
||||
+
|
||||
Default is 30 seconds.
|
||||
|
||||
[[changeMerge]]Section changeMerge
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -46,6 +46,7 @@ public class GerritConfig implements Cloneable {
|
||||
protected boolean testChangeMerge;
|
||||
protected String anonymousCowardName;
|
||||
protected int suggestFrom;
|
||||
protected int changeUpdateDelay;
|
||||
|
||||
public String getRegisterUrl() {
|
||||
return registerUrl;
|
||||
@ -225,4 +226,12 @@ public class GerritConfig implements Cloneable {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getChangeUpdateDelay() {
|
||||
return changeUpdateDelay;
|
||||
}
|
||||
|
||||
public void setChangeUpdateDelay(int seconds) {
|
||||
changeUpdateDelay = seconds;
|
||||
}
|
||||
}
|
||||
|
@ -18,16 +18,20 @@ import com.google.gwt.dom.client.Document;
|
||||
import com.google.gwt.dom.client.Element;
|
||||
import com.google.gwt.dom.client.Node;
|
||||
import com.google.gwt.event.dom.client.HasKeyPressHandlers;
|
||||
import com.google.gwt.event.dom.client.HasMouseMoveHandlers;
|
||||
import com.google.gwt.event.dom.client.KeyPressEvent;
|
||||
import com.google.gwt.event.dom.client.KeyPressHandler;
|
||||
import com.google.gwt.event.dom.client.MouseMoveEvent;
|
||||
import com.google.gwt.event.dom.client.MouseMoveHandler;
|
||||
import com.google.gwt.event.shared.HandlerRegistration;
|
||||
import com.google.gwt.user.client.ui.RootPanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
|
||||
class DocWidget extends Widget implements HasKeyPressHandlers {
|
||||
public class DocWidget extends Widget
|
||||
implements HasKeyPressHandlers, HasMouseMoveHandlers {
|
||||
private static DocWidget me;
|
||||
|
||||
static DocWidget get() {
|
||||
public static DocWidget get() {
|
||||
if (me == null) {
|
||||
me = new DocWidget();
|
||||
}
|
||||
@ -45,6 +49,11 @@ class DocWidget extends Widget implements HasKeyPressHandlers {
|
||||
return addDomHandler(handler, KeyPressEvent.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerRegistration addMouseMoveHandler(MouseMoveHandler handler) {
|
||||
return addDomHandler(handler, MouseMoveEvent.getType());
|
||||
}
|
||||
|
||||
private static Node docnode() {
|
||||
return Document.get();
|
||||
}
|
||||
|
@ -261,6 +261,10 @@ public class Gerrit implements EntryPoint {
|
||||
return topMenu.isVisible();
|
||||
}
|
||||
|
||||
public static RootPanel getBottomMenu() {
|
||||
return bottomMenu;
|
||||
}
|
||||
|
||||
/** Get the public configuration data used by this Gerrit instance. */
|
||||
public static GerritConfig getConfig() {
|
||||
return myConfig;
|
||||
|
@ -25,6 +25,7 @@ import com.google.gerrit.client.changes.ChangeInfo.LabelInfo;
|
||||
import com.google.gerrit.client.changes.ChangeInfo.MergeableInfo;
|
||||
import com.google.gerrit.client.changes.ChangeInfo.MessageInfo;
|
||||
import com.google.gerrit.client.changes.ChangeInfo.RevisionInfo;
|
||||
import com.google.gerrit.client.changes.ChangeList;
|
||||
import com.google.gerrit.client.changes.StarredChanges;
|
||||
import com.google.gerrit.client.changes.Util;
|
||||
import com.google.gerrit.client.diff.DiffApi;
|
||||
@ -36,10 +37,12 @@ import com.google.gerrit.client.rpc.GerritCallback;
|
||||
import com.google.gerrit.client.rpc.NativeMap;
|
||||
import com.google.gerrit.client.rpc.NativeString;
|
||||
import com.google.gerrit.client.rpc.Natives;
|
||||
import com.google.gerrit.client.rpc.RestApi;
|
||||
import com.google.gerrit.client.rpc.ScreenLoadCallback;
|
||||
import com.google.gerrit.client.ui.ChangeLink;
|
||||
import com.google.gerrit.client.ui.CommentLinkProcessor;
|
||||
import com.google.gerrit.client.ui.Screen;
|
||||
import com.google.gerrit.client.ui.UserActivityMonitor;
|
||||
import com.google.gerrit.common.PageLinks;
|
||||
import com.google.gerrit.common.changes.ListChangesOption;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
@ -54,6 +57,8 @@ import com.google.gwt.dom.client.Element;
|
||||
import com.google.gwt.event.dom.client.ChangeEvent;
|
||||
import com.google.gwt.event.dom.client.ClickEvent;
|
||||
import com.google.gwt.event.dom.client.KeyPressEvent;
|
||||
import com.google.gwt.event.logical.shared.CloseEvent;
|
||||
import com.google.gwt.event.logical.shared.CloseHandler;
|
||||
import com.google.gwt.event.logical.shared.ValueChangeEvent;
|
||||
import com.google.gwt.event.shared.HandlerRegistration;
|
||||
import com.google.gwt.resources.client.CssResource;
|
||||
@ -65,6 +70,7 @@ import com.google.gwt.user.client.ui.Button;
|
||||
import com.google.gwt.user.client.ui.FlowPanel;
|
||||
import com.google.gwt.user.client.ui.HTMLPanel;
|
||||
import com.google.gwt.user.client.ui.ListBox;
|
||||
import com.google.gwt.user.client.ui.PopupPanel;
|
||||
import com.google.gwt.user.client.ui.ToggleButton;
|
||||
import com.google.gwt.user.client.ui.UIObject;
|
||||
import com.google.gwtexpui.clippy.client.CopyableLabel;
|
||||
@ -73,6 +79,7 @@ import com.google.gwtexpui.globalkey.client.KeyCommand;
|
||||
import com.google.gwtexpui.globalkey.client.KeyCommandSet;
|
||||
import com.google.gwtorm.client.KeyUtil;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
@ -97,11 +104,15 @@ public class ChangeScreen2 extends Screen {
|
||||
|
||||
private final Change.Id changeId;
|
||||
private String revision;
|
||||
private ChangeInfo changeInfo;
|
||||
private CommentLinkProcessor commentLinkProcessor;
|
||||
|
||||
private KeyCommandSet keysNavigation;
|
||||
private KeyCommandSet keysAction;
|
||||
private List<HandlerRegistration> keys = new ArrayList<HandlerRegistration>(2);
|
||||
private List<HandlerRegistration> handlers = new ArrayList<HandlerRegistration>(4);
|
||||
private UpdateCheckTimer updateCheck;
|
||||
private Timestamp lastDisplayedUpdate;
|
||||
private UpdateAvailableBar updateAvailable;
|
||||
|
||||
@UiField Style style;
|
||||
@UiField ToggleButton star;
|
||||
@ -143,25 +154,40 @@ public class ChangeScreen2 extends Screen {
|
||||
@Override
|
||||
protected void onLoad() {
|
||||
super.onLoad();
|
||||
ChangeApi.detail(changeId.get(),
|
||||
EnumSet.of(
|
||||
ListChangesOption.ALL_REVISIONS,
|
||||
ListChangesOption.CURRENT_ACTIONS),
|
||||
new GerritCallback<ChangeInfo>() {
|
||||
@Override
|
||||
public void onSuccess(ChangeInfo info) {
|
||||
info.init();
|
||||
loadConfigInfo(info);
|
||||
}
|
||||
});
|
||||
loadChangeInfo(true, new GerritCallback<ChangeInfo>() {
|
||||
@Override
|
||||
public void onSuccess(ChangeInfo info) {
|
||||
info.init();
|
||||
loadConfigInfo(info);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void loadChangeInfo(boolean fg, AsyncCallback<ChangeInfo> cb) {
|
||||
RestApi call = ChangeApi.detail(changeId.get());
|
||||
ChangeList.addOptions(call, EnumSet.of(
|
||||
ListChangesOption.ALL_REVISIONS,
|
||||
ListChangesOption.CURRENT_ACTIONS));
|
||||
if (!fg) {
|
||||
call.background();
|
||||
}
|
||||
call.get(cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onUnload() {
|
||||
for (HandlerRegistration h : keys) {
|
||||
if (updateAvailable != null) {
|
||||
updateAvailable.hide(true);
|
||||
updateAvailable = null;
|
||||
}
|
||||
if (updateCheck != null) {
|
||||
updateCheck.cancel();
|
||||
updateCheck = null;
|
||||
}
|
||||
for (HandlerRegistration h : handlers) {
|
||||
h.removeHandler();
|
||||
}
|
||||
keys.clear();
|
||||
handlers.clear();
|
||||
super.onUnload();
|
||||
}
|
||||
|
||||
@ -207,8 +233,8 @@ public class ChangeScreen2 extends Screen {
|
||||
@Override
|
||||
public void registerKeys() {
|
||||
super.registerKeys();
|
||||
keys.add(GlobalKey.add(this, keysNavigation));
|
||||
keys.add(GlobalKey.add(this, keysAction));
|
||||
handlers.add(GlobalKey.add(this, keysNavigation));
|
||||
handlers.add(GlobalKey.add(this, keysAction));
|
||||
files.registerKeys();
|
||||
}
|
||||
|
||||
@ -220,6 +246,7 @@ public class ChangeScreen2 extends Screen {
|
||||
if (prior != null && prior.startsWith("/c/")) {
|
||||
scrollToPath(prior.substring(3));
|
||||
}
|
||||
startPoller();
|
||||
}
|
||||
|
||||
private void scrollToPath(String token) {
|
||||
@ -405,6 +432,8 @@ public class ChangeScreen2 extends Screen {
|
||||
}
|
||||
|
||||
private void renderChangeInfo(ChangeInfo info) {
|
||||
changeInfo = info;
|
||||
lastDisplayedUpdate = info.updated();
|
||||
statusText.setInnerText(Util.toLongString(info.status()));
|
||||
boolean current = info.status().isOpen()
|
||||
&& revision.equals(info.current_revision());
|
||||
@ -539,4 +568,53 @@ public class ChangeScreen2 extends Screen {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void showUpdates(ChangeInfo newInfo) {
|
||||
if (!isAttached() || newInfo.updated().equals(lastDisplayedUpdate)) {
|
||||
return;
|
||||
}
|
||||
|
||||
JsArray<MessageInfo> om = changeInfo.messages();
|
||||
JsArray<MessageInfo> nm = newInfo.messages();
|
||||
|
||||
if (om == null) {
|
||||
om = JsArray.createArray().cast();
|
||||
}
|
||||
if (nm == null) {
|
||||
nm = JsArray.createArray().cast();
|
||||
}
|
||||
|
||||
if (updateAvailable == null) {
|
||||
updateAvailable = new UpdateAvailableBar() {
|
||||
@Override
|
||||
void onShow() {
|
||||
reload.reload();
|
||||
}
|
||||
|
||||
void onIgnore(Timestamp newTime) {
|
||||
lastDisplayedUpdate = newTime;
|
||||
}
|
||||
};
|
||||
updateAvailable.addCloseHandler(new CloseHandler<PopupPanel>() {
|
||||
@Override
|
||||
public void onClose(CloseEvent<PopupPanel> event) {
|
||||
updateAvailable = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
updateAvailable.set(
|
||||
Natives.asList(nm).subList(om.length(), nm.length()),
|
||||
newInfo.updated());
|
||||
if (!updateAvailable.isShowing()) {
|
||||
updateAvailable.popup();
|
||||
}
|
||||
}
|
||||
|
||||
private void startPoller() {
|
||||
if (Gerrit.isSignedIn() && 0 < Gerrit.getConfig().getChangeUpdateDelay()) {
|
||||
updateCheck = new UpdateCheckTimer(this);
|
||||
updateCheck.schedule();
|
||||
handlers.add(UserActivityMonitor.addValueChangeHandler(updateCheck));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ class Message extends Composite {
|
||||
}
|
||||
}
|
||||
|
||||
private static String authorName(MessageInfo info) {
|
||||
static String authorName(MessageInfo info) {
|
||||
if (info.author() != null) {
|
||||
if (info.author().name() != null) {
|
||||
return info.author().name();
|
||||
|
@ -0,0 +1,151 @@
|
||||
// Copyright (C) 2013 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.client.change;
|
||||
|
||||
import com.google.gerrit.client.Gerrit;
|
||||
import com.google.gerrit.client.changes.ChangeInfo.MessageInfo;
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.dom.client.Element;
|
||||
import com.google.gwt.event.dom.client.ClickEvent;
|
||||
import com.google.gwt.event.logical.shared.ResizeEvent;
|
||||
import com.google.gwt.event.logical.shared.ResizeHandler;
|
||||
import com.google.gwt.event.shared.HandlerRegistration;
|
||||
import com.google.gwt.resources.client.CssResource;
|
||||
import com.google.gwt.uibinder.client.UiBinder;
|
||||
import com.google.gwt.uibinder.client.UiField;
|
||||
import com.google.gwt.uibinder.client.UiHandler;
|
||||
import com.google.gwt.user.client.Window;
|
||||
import com.google.gwt.user.client.Window.ScrollEvent;
|
||||
import com.google.gwt.user.client.Window.ScrollHandler;
|
||||
import com.google.gwt.user.client.ui.Anchor;
|
||||
import com.google.gwt.user.client.ui.HTMLPanel;
|
||||
import com.google.gwt.user.client.ui.PopupPanel;
|
||||
import com.google.gwt.user.client.ui.RootPanel;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
/** Displays the "New Message From ..." panel in bottom right on updates. */
|
||||
abstract class UpdateAvailableBar extends PopupPanel {
|
||||
interface Binder extends UiBinder<HTMLPanel, UpdateAvailableBar> {}
|
||||
private static Binder uiBinder = GWT.create(Binder.class);
|
||||
|
||||
static interface Style extends CssResource {
|
||||
String popup();
|
||||
}
|
||||
|
||||
private Timestamp updated;
|
||||
private HandlerRegistration resizer;
|
||||
private HandlerRegistration scroller;
|
||||
|
||||
@UiField Style style;
|
||||
@UiField Element author;
|
||||
@UiField Anchor show;
|
||||
@UiField Anchor ignore;
|
||||
|
||||
UpdateAvailableBar() {
|
||||
super(/* autoHide = */ false, /* modal = */ false);
|
||||
add(uiBinder.createAndBindUi(this));
|
||||
setStyleName(style.popup());
|
||||
}
|
||||
|
||||
void set(List<MessageInfo> newMessages, Timestamp newTime) {
|
||||
HashSet<Integer> seen = new HashSet<Integer>();
|
||||
StringBuilder r = new StringBuilder();
|
||||
for (MessageInfo m : newMessages) {
|
||||
int a = m.author() != null ? m.author()._account_id() : 0;
|
||||
if (seen.add(a)) {
|
||||
if (r.length() > 0) {
|
||||
r.append(", ");
|
||||
}
|
||||
r.append(Message.authorName(m));
|
||||
}
|
||||
}
|
||||
author.setInnerText(r.toString());
|
||||
updated = newTime;
|
||||
|
||||
if (isShowing()) {
|
||||
setPopupPosition(
|
||||
Window.getScrollLeft() + Window.getClientWidth() - getOffsetWidth(),
|
||||
Window.getScrollTop() + Window.getClientHeight() - getOffsetHeight());
|
||||
}
|
||||
}
|
||||
|
||||
void popup() {
|
||||
setPopupPositionAndShow(new PositionCallback() {
|
||||
@Override
|
||||
public void setPosition(int w, int h) {
|
||||
w += 7; // Initial information is wrong, adjust with some slop.
|
||||
h += 19;
|
||||
setPopupPosition(
|
||||
Window.getScrollLeft() + Window.getClientWidth() - w,
|
||||
Window.getScrollTop() + Window.getClientHeight() - h);
|
||||
}
|
||||
});
|
||||
if (resizer == null) {
|
||||
resizer = Window.addResizeHandler(new ResizeHandler() {
|
||||
@Override
|
||||
public void onResize(ResizeEvent event) {
|
||||
setPopupPosition(
|
||||
Window.getScrollLeft() + event.getWidth() - getOffsetWidth(),
|
||||
Window.getScrollTop() + event.getHeight() - getOffsetHeight());
|
||||
}
|
||||
});
|
||||
}
|
||||
if (scroller == null) {
|
||||
scroller = Window.addWindowScrollHandler(new ScrollHandler() {
|
||||
@Override
|
||||
public void onWindowScroll(ScrollEvent event) {
|
||||
RootPanel b = Gerrit.getBottomMenu();
|
||||
int br = b.getAbsoluteLeft() + b.getOffsetWidth();
|
||||
int bp = b.getAbsoluteTop() + b.getOffsetHeight();
|
||||
int wr = event.getScrollLeft() + Window.getClientWidth();
|
||||
int wp = event.getScrollTop() + Window.getClientHeight();
|
||||
setPopupPosition(
|
||||
Math.min(br, wr) - getOffsetWidth(),
|
||||
Math.min(bp, wp) - getOffsetHeight());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hide() {
|
||||
if (resizer != null) {
|
||||
resizer.removeHandler();
|
||||
resizer = null;
|
||||
}
|
||||
if (scroller != null) {
|
||||
scroller.removeHandler();
|
||||
scroller = null;
|
||||
}
|
||||
super.hide();
|
||||
}
|
||||
|
||||
@UiHandler("show")
|
||||
void onShow(ClickEvent e) {
|
||||
onShow();
|
||||
}
|
||||
|
||||
@UiHandler("ignore")
|
||||
void onIgnore(ClickEvent e) {
|
||||
onIgnore(updated);
|
||||
hide();
|
||||
}
|
||||
|
||||
abstract void onShow();
|
||||
abstract void onIgnore(Timestamp newTime);
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright (C) 2013 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<ui:UiBinder
|
||||
xmlns:ui='urn:ui:com.google.gwt.uibinder'
|
||||
xmlns:c='urn:import:com.google.gwtexpui.globalkey.client'
|
||||
xmlns:g='urn:import:com.google.gwt.user.client.ui'>
|
||||
<ui:style type='com.google.gerrit.client.change.UpdateAvailableBar.Style'>
|
||||
.popup {
|
||||
padding: 5px;
|
||||
}
|
||||
.bar {
|
||||
background-color: #fff1a8;
|
||||
border: 1px solid #ccc;
|
||||
padding: 5px 10px;
|
||||
font-size: 80%;
|
||||
color: #222;
|
||||
white-space: nowrap;
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
.action {
|
||||
color: #222;
|
||||
display: inline-block;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
</ui:style>
|
||||
<g:HTMLPanel styleName='{style.bar}'>
|
||||
<ui:msg>Update from <span ui:field='author'/></ui:msg>
|
||||
<g:Anchor ui:field='show' styleName='{style.action}'>
|
||||
<ui:msg>Show</ui:msg>
|
||||
</g:Anchor>
|
||||
<g:Anchor ui:field='ignore' styleName='{style.action}'>
|
||||
<ui:msg>Ignore</ui:msg>
|
||||
</g:Anchor>
|
||||
</g:HTMLPanel>
|
||||
</ui:UiBinder>
|
@ -0,0 +1,95 @@
|
||||
// Copyright (C) 2013 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.client.change;
|
||||
|
||||
import com.google.gerrit.client.Gerrit;
|
||||
import com.google.gerrit.client.changes.ChangeInfo;
|
||||
import com.google.gerrit.client.ui.UserActivityMonitor;
|
||||
import com.google.gwt.event.logical.shared.ValueChangeEvent;
|
||||
import com.google.gwt.event.logical.shared.ValueChangeHandler;
|
||||
import com.google.gwt.user.client.Timer;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
|
||||
class UpdateCheckTimer extends Timer implements ValueChangeHandler<Boolean> {
|
||||
private static final int MAX_PERIOD = 3 * 60 * 1000;
|
||||
private static final int IDLE_PERIOD = 2 * 3600 * 1000;
|
||||
private static final int POLL_PERIOD =
|
||||
Gerrit.getConfig().getChangeUpdateDelay() * 1000;
|
||||
|
||||
private final ChangeScreen2 screen;
|
||||
private int delay;
|
||||
private boolean running;
|
||||
|
||||
UpdateCheckTimer(ChangeScreen2 screen) {
|
||||
this.screen = screen;
|
||||
this.delay = POLL_PERIOD;
|
||||
}
|
||||
|
||||
void schedule() {
|
||||
scheduleRepeating(delay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!screen.isAttached()) {
|
||||
// screen should have cancelled this timer.
|
||||
cancel();
|
||||
return;
|
||||
} else if (running) {
|
||||
return;
|
||||
}
|
||||
|
||||
running = true;
|
||||
screen.loadChangeInfo(false, new AsyncCallback<ChangeInfo>() {
|
||||
@Override
|
||||
public void onSuccess(ChangeInfo info) {
|
||||
running = false;
|
||||
screen.showUpdates(info);
|
||||
|
||||
int d = UserActivityMonitor.isActive()
|
||||
? POLL_PERIOD
|
||||
: IDLE_PERIOD;
|
||||
if (d != delay) {
|
||||
delay = d;
|
||||
schedule();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
// On failures increase the delay time and try again,
|
||||
// but place an upper bound on the delay.
|
||||
running = false;
|
||||
delay = (int) Math.max(
|
||||
delay * (1.5 + Math.random()),
|
||||
UserActivityMonitor.isActive()
|
||||
? MAX_PERIOD
|
||||
: IDLE_PERIOD + MAX_PERIOD);
|
||||
schedule();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onValueChange(ValueChangeEvent<Boolean> event) {
|
||||
if (event.getValue()) {
|
||||
delay = POLL_PERIOD;
|
||||
run();
|
||||
} else {
|
||||
delay = IDLE_PERIOD;
|
||||
}
|
||||
schedule();
|
||||
}
|
||||
}
|
@ -16,13 +16,10 @@ package com.google.gerrit.client.changes;
|
||||
|
||||
import com.google.gerrit.client.rpc.NativeString;
|
||||
import com.google.gerrit.client.rpc.RestApi;
|
||||
import com.google.gerrit.common.changes.ListChangesOption;
|
||||
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||
import com.google.gwt.core.client.JavaScriptObject;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* A collection of static methods which work on the Gerrit REST API for specific
|
||||
* changes.
|
||||
@ -68,16 +65,7 @@ public class ChangeApi {
|
||||
detail(id).get(cb);
|
||||
}
|
||||
|
||||
public static void detail(int id, EnumSet<ListChangesOption> options,
|
||||
AsyncCallback<ChangeInfo> cb) {
|
||||
RestApi call = detail(id);
|
||||
if (!options.isEmpty()) {
|
||||
ChangeList.addOptions(call, options);
|
||||
}
|
||||
call.get(cb);
|
||||
}
|
||||
|
||||
private static RestApi detail(int id) {
|
||||
public static RestApi detail(int id) {
|
||||
return call(id, "detail");
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,113 @@
|
||||
// Copyright (C) 2013 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.client.ui;
|
||||
|
||||
import com.google.gwt.core.client.Scheduler;
|
||||
import com.google.gwt.core.client.Scheduler.RepeatingCommand;
|
||||
import com.google.gwt.event.dom.client.KeyPressEvent;
|
||||
import com.google.gwt.event.dom.client.KeyPressHandler;
|
||||
import com.google.gwt.event.dom.client.MouseMoveEvent;
|
||||
import com.google.gwt.event.dom.client.MouseMoveHandler;
|
||||
import com.google.gwt.event.logical.shared.HasValueChangeHandlers;
|
||||
import com.google.gwt.event.logical.shared.ValueChangeEvent;
|
||||
import com.google.gwt.event.logical.shared.ValueChangeHandler;
|
||||
import com.google.gwt.event.shared.EventBus;
|
||||
import com.google.gwt.event.shared.GwtEvent;
|
||||
import com.google.gwt.event.shared.HandlerRegistration;
|
||||
import com.google.gwt.event.shared.SimpleEventBus;
|
||||
import com.google.gwt.user.client.History;
|
||||
import com.google.gwtexpui.globalkey.client.DocWidget;
|
||||
|
||||
/** Checks for user keyboard and mouse activity. */
|
||||
public class UserActivityMonitor {
|
||||
private static final long TIMEOUT = 10 * 60 * 1000;
|
||||
private static final MonitorImpl impl;
|
||||
|
||||
/**
|
||||
* @return true if there has been keyboard and/or mouse activity in recent
|
||||
* enough history to believe a user is still controlling this session.
|
||||
*/
|
||||
public static boolean isActive() {
|
||||
return impl.active || impl.recent;
|
||||
}
|
||||
|
||||
public static HandlerRegistration addValueChangeHandler(
|
||||
ValueChangeHandler<Boolean> handler) {
|
||||
return impl.addValueChangeHandler(handler);
|
||||
}
|
||||
|
||||
static {
|
||||
impl = new MonitorImpl();
|
||||
DocWidget.get().addKeyPressHandler(impl);
|
||||
DocWidget.get().addMouseMoveHandler(impl);
|
||||
History.addValueChangeHandler(impl);
|
||||
Scheduler.get().scheduleFixedDelay(impl, 60 * 1000);
|
||||
}
|
||||
|
||||
private UserActivityMonitor() {
|
||||
}
|
||||
|
||||
private static class MonitorImpl implements RepeatingCommand,
|
||||
KeyPressHandler, MouseMoveHandler, ValueChangeHandler<String>,
|
||||
HasValueChangeHandlers<Boolean> {
|
||||
private final EventBus bus = new SimpleEventBus();
|
||||
private boolean recent = true;
|
||||
private boolean active = true;
|
||||
private long last = System.currentTimeMillis();
|
||||
|
||||
@Override
|
||||
public void onKeyPress(KeyPressEvent event) {
|
||||
recent = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMouseMove(MouseMoveEvent event) {
|
||||
recent = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onValueChange(ValueChangeEvent<String> event) {
|
||||
recent = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute() {
|
||||
long now = System.currentTimeMillis();
|
||||
if (recent) {
|
||||
if (!active) {
|
||||
ValueChangeEvent.fire(this, active);
|
||||
}
|
||||
recent = false;
|
||||
active = true;
|
||||
last = now;
|
||||
} else if (active && (now - last) > TIMEOUT) {
|
||||
active = false;
|
||||
ValueChangeEvent.fire(this, false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerRegistration addValueChangeHandler(
|
||||
ValueChangeHandler<Boolean> handler) {
|
||||
return bus.addHandler(ValueChangeEvent.getType(), handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireEvent(GwtEvent<?> event) {
|
||||
bus.fireEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@ import com.google.gerrit.server.account.Realm;
|
||||
import com.google.gerrit.server.config.AllProjectsName;
|
||||
import com.google.gerrit.server.config.AnonymousCowardName;
|
||||
import com.google.gerrit.server.config.AuthConfig;
|
||||
import com.google.gerrit.server.config.ConfigUtil;
|
||||
import com.google.gerrit.server.config.DownloadConfig;
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gerrit.server.contact.ContactStore;
|
||||
@ -35,6 +36,7 @@ import org.eclipse.jgit.lib.Config;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
@ -115,6 +117,8 @@ class GerritConfigProvider implements Provider<GerritConfig> {
|
||||
"test", false));
|
||||
config.setAnonymousCowardName(anonymousCowardName);
|
||||
config.setSuggestFrom(cfg.getInt("suggest", "from", 0));
|
||||
config.setChangeUpdateDelay((int) ConfigUtil.getTimeUnit(
|
||||
cfg, "change", null, "updateDelay", 30, TimeUnit.SECONDS));
|
||||
|
||||
config.setReportBugUrl(cfg.getString("gerrit", null, "reportBugUrl"));
|
||||
if (config.getReportBugUrl() == null) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user