Cleanup the style and layout for the SSH key tab in account settings

Checkboxes can now be used to delete multiple keys at once, as that
UI is much easier to understand than using the cursor to move onto
the row you want to delete.

Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
Shawn O. Pearce
2008-12-26 17:57:14 -08:00
parent cf56e8a4c3
commit eb3656fc7b
8 changed files with 163 additions and 102 deletions

View File

@@ -18,4 +18,23 @@ import com.google.gwt.i18n.client.Constants;
public interface AccountConstants extends Constants {
String accountSettingsHeading();
String fullName();
String preferredEmail();
String registeredOn();
String tabPreferences();
String tabSshKeys();
String tabAgreements();
String buttonDeleteSshKey();
String buttonAddSshKey();
String sshKeyAlgorithm();
String sshKeyKey();
String sshKeyComment();
String sshKeyLastUsed();
String sshKeyStored();
String addSshKeyPanelHeader();
}

View File

@@ -1 +1,20 @@
accountSettingsHeading = Account Settings
fullName = Full Name
preferredEmail = Email Address
registeredOn = Registered
tabPreferences = Preferences
tabSshKeys = SSH Keys
tabAgreements = Agreements
buttonDeleteSshKey = Delete
buttonAddSshKey = Add
sshKeyAlgorithm = Algorithm
sshKeyKey = Key
sshKeyComment = Comment
sshKeyLastUsed = Last Used
sshKeyStored = Stored
addSshKeyPanelHeader = Add SSH Key

View File

@@ -21,16 +21,20 @@ import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwtjsonrpc.client.RemoteJsonService;
import com.google.gwtjsonrpc.client.VoidResult;
import java.util.List;
import java.util.Set;
public interface AccountService extends RemoteJsonService {
@SignInRequired
void myAccount(AsyncCallback<Account> callback);
@SignInRequired
void mySshKeys(AsyncCallback<SshKeyList> callback);
void mySshKeys(AsyncCallback<List<AccountSshKey>> callback);
@SignInRequired
void addSshKey(String keyText, AsyncCallback<AccountSshKey> callback);
@SignInRequired
void deleteSshKey(AccountSshKey.Id id, AsyncCallback<VoidResult> callback);
void deleteSshKeys(Set<AccountSshKey.Id> ids,
AsyncCallback<VoidResult> callback);
}

View File

@@ -24,8 +24,11 @@ import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwtjsonrpc.client.VoidResult;
import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.client.SchemaFactory;
import com.google.gwtorm.client.Transaction;
import java.util.Collections;
import java.util.List;
import java.util.Set;
public class AccountServiceImpl extends BaseServiceImplementation implements
AccountService {
@@ -41,11 +44,10 @@ public class AccountServiceImpl extends BaseServiceImplementation implements
});
}
public void mySshKeys(final AsyncCallback<SshKeyList> callback) {
run(callback, new Action<SshKeyList>() {
public SshKeyList run(ReviewDb db) throws OrmException {
return new SshKeyList(db.accountSshKeys().byAccount(
RpcUtil.getAccountId()).toList());
public void mySshKeys(final AsyncCallback<List<AccountSshKey>> callback) {
run(callback, new Action<List<AccountSshKey>>() {
public List<AccountSshKey> run(ReviewDb db) throws OrmException {
return db.accountSshKeys().byAccount(RpcUtil.getAccountId()).toList();
}
});
}
@@ -68,16 +70,22 @@ public class AccountServiceImpl extends BaseServiceImplementation implements
});
}
public void deleteSshKey(final AccountSshKey.Id id,
public void deleteSshKeys(final Set<AccountSshKey.Id> ids,
final AsyncCallback<VoidResult> callback) {
run(callback, new Action<VoidResult>() {
public VoidResult run(final ReviewDb db) throws OrmException, Failure {
final Account.Id me = RpcUtil.getAccountId();
if (!me.equals(id.getParentKey()))
throw new Failure(new NoSuchEntityException());
for (final AccountSshKey.Id keyId : ids) {
if (!me.equals(keyId.getParentKey()))
throw new Failure(new NoSuchEntityException());
}
final AccountSshKey k = db.accountSshKeys().get(id);
if (k != null) db.accountSshKeys().delete(Collections.singleton(k));
final List<AccountSshKey> k = db.accountSshKeys().get(ids).toList();
if (!k.isEmpty()) {
final Transaction txn = db.beginTransaction();
db.accountSshKeys().delete(k, txn);
txn.commit();
}
return VoidResult.INSTANCE;
}

View File

@@ -40,11 +40,12 @@ public class AccountSettings extends AccountScreen {
info = new Grid(3, 2);
info.setStyleName("gerrit-InfoBlock");
info.addStyleName("gerrit-AccountInfoBlock");
add(info);
infoRow(0, "Full Name");
infoRow(1, "Email Address");
infoRow(2, "Registered");
infoRow(0, Util.C.fullName());
infoRow(1, Util.C.preferredEmail());
infoRow(2, Util.C.registeredOn());
final CellFormatter fmt = info.getCellFormatter();
fmt.addStyleName(0, 0, "topmost");
@@ -61,9 +62,9 @@ public class AccountSettings extends AccountScreen {
tabs = new TabPanel();
tabs.setWidth("100%");
tabs.add(prefsPanel, "Preferences");
tabs.add(keysPanel, "SSH Keys");
tabs.add(agreementsPanel, "Agreements");
tabs.add(prefsPanel, Util.C.tabPreferences());
tabs.add(keysPanel, Util.C.tabSshKeys());
tabs.add(agreementsPanel, Util.C.tabAgreements());
add(tabs);
tabs.selectTab(0);

View File

@@ -1,30 +0,0 @@
// Copyright 2008 Google Inc.
//
// 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.account;
import com.google.gerrit.client.reviewdb.AccountSshKey;
import java.util.List;
final class SshKeyList {
List<AccountSshKey> keys;
protected SshKeyList() {
}
public SshKeyList(final List<AccountSshKey> k) {
keys = k;
}
}

View File

@@ -19,16 +19,20 @@ import com.google.gerrit.client.reviewdb.AccountSshKey;
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.ui.FancyFlexTable;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SourcesTableEvents;
import com.google.gwt.user.client.ui.TableListener;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
import com.google.gwtjsonrpc.client.VoidResult;
import java.util.HashSet;
import java.util.List;
public class SshKeyPanel extends Composite {
@@ -45,11 +49,10 @@ public class SshKeyPanel extends Composite {
body.add(keys);
{
final FlowPanel fp = new FlowPanel();
delSel = new Button("Delete");
delSel.setEnabled(false);
delSel = new Button(Util.C.buttonDeleteSshKey());
delSel.addClickListener(new ClickListener() {
public void onClick(final Widget sender) {
keys.deleteCurrent();
keys.deleteChecked();
}
});
fp.add(delSel);
@@ -57,13 +60,16 @@ public class SshKeyPanel extends Composite {
}
{
final FlowPanel fp = new FlowPanel();
final VerticalPanel fp = new VerticalPanel();
fp.setStyleName("gerrit-AddSshKeyPanel");
fp.add(new Label(Util.C.addSshKeyPanelHeader()));
addTxt = new TextArea();
addTxt.setVisibleLines(3);
addTxt.setCharacterWidth(60);
addTxt.setVisibleLines(12);
addTxt.setCharacterWidth(80);
fp.add(addTxt);
addNew = new Button("Add ...");
addNew = new Button(Util.C.buttonAddSshKey());
addNew.addClickListener(new ClickListener() {
public void onClick(final Widget sender) {
doAddNew();
@@ -105,9 +111,9 @@ public class SshKeyPanel extends Composite {
}
public void update() {
Util.ACCOUNT_SVC.mySshKeys(new GerritCallback<SshKeyList>() {
public void onSuccess(final SshKeyList result) {
keys.display(result.keys);
Util.ACCOUNT_SVC.mySshKeys(new GerritCallback<List<AccountSshKey>>() {
public void onSuccess(final List<AccountSshKey> result) {
keys.display(result);
keys.finishDisplay(true);
}
});
@@ -115,36 +121,26 @@ public class SshKeyPanel extends Composite {
private class SshKeyTable extends FancyFlexTable<AccountSshKey> {
SshKeyTable() {
table.setText(0, 1, "Algorithm");
table.setText(0, 2, "Key");
table.setText(0, 3, "Comment");
table.setText(0, 4, "Last Used");
table.setText(0, 5, "Stored");
table.setText(0, 2, Util.C.sshKeyAlgorithm());
table.setText(0, 3, Util.C.sshKeyKey());
table.setText(0, 4, Util.C.sshKeyComment());
table.setText(0, 5, Util.C.sshKeyLastUsed());
table.setText(0, 6, Util.C.sshKeyStored());
table.addTableListener(new TableListener() {
public void onCellClicked(SourcesTableEvents sender, int row, int cell) {
if (getRowItem(row) != null) {
if (cell != 1 && getRowItem(row) != null) {
movePointerTo(row);
}
}
});
final FlexCellFormatter fmt = table.getFlexCellFormatter();
fmt.addStyleName(0, 1, S_DATA_HEADER);
fmt.addStyleName(0, 1, S_ICON_HEADER);
fmt.addStyleName(0, 2, S_DATA_HEADER);
fmt.addStyleName(0, 3, S_DATA_HEADER);
fmt.addStyleName(0, 4, S_DATA_HEADER);
fmt.addStyleName(0, 5, S_DATA_HEADER);
}
@Override
protected void movePointerTo(final int newRow) {
super.movePointerTo(newRow);
if (0 <= newRow && newRow < table.getRowCount()
&& getRowItem(newRow) != null) {
delSel.setEnabled(true);
} else {
delSel.setEnabled(false);
}
fmt.addStyleName(0, 6, S_DATA_HEADER);
}
@Override
@@ -152,28 +148,54 @@ public class SshKeyPanel extends Composite {
return item.getKey();
}
void deleteCurrent() {
final int row = getCurrentRow();
if (0 <= row && row < table.getRowCount()) {
final AccountSshKey k = getRowItem(row);
if (k != null) {
Util.ACCOUNT_SVC.deleteSshKey(k.getKey(),
new GerritCallback<VoidResult>() {
public void onSuccess(final VoidResult result) {
int jumpTo = -1;
for (int i = 0; i < table.getRowCount(); i++) {
if (getRowItem(i) == k) {
table.removeRow(i);
movePointerTo(jumpTo);
break;
} else if (getRowItem(i) != null) {
jumpTo = i;
}
}
}
});
@Override
protected boolean onKeyPress(final char keyCode, final int modifiers) {
if (super.onKeyPress(keyCode, modifiers)) {
return true;
}
if (modifiers == 0) {
switch (keyCode) {
case 's':
case 'c':
toggleCurrentRow();
return true;
}
}
return false;
}
@Override
protected void onOpenItem(final AccountSshKey item) {
toggleCurrentRow();
}
private void toggleCurrentRow() {
final CheckBox cb = (CheckBox) table.getWidget(getCurrentRow(), 1);
cb.setChecked(!cb.isChecked());
}
void deleteChecked() {
final HashSet<AccountSshKey.Id> ids = new HashSet<AccountSshKey.Id>();
for (int row = 1; row < table.getRowCount(); row++) {
final AccountSshKey k = getRowItem(row);
if (k != null && ((CheckBox) table.getWidget(row, 1)).isChecked()) {
ids.add(k.getKey());
}
}
if (!ids.isEmpty()) {
Util.ACCOUNT_SVC.deleteSshKeys(ids, new GerritCallback<VoidResult>() {
public void onSuccess(final VoidResult result) {
for (int row = 1; row < table.getRowCount();) {
final AccountSshKey k = getRowItem(row);
if (k != null && ids.contains(k.getKey())) {
table.removeRow(row);
} else {
row++;
}
}
}
});
}
}
void display(final List<AccountSshKey> result) {
@@ -189,14 +211,19 @@ public class SshKeyPanel extends Composite {
final int row = table.getRowCount();
table.insertRow(row);
table.setText(row, 1, k.getAlgorithm());
table.setText(row, 2, elide(k.getEncodedKey()));
table.setText(row, 3, k.getComment());
table.setText(row, 4, FormatUtil.mediumFormat(k.getLastUsedOn()));
table.setText(row, 5, FormatUtil.mediumFormat(k.getStoredOn()));
table.setWidget(row, 1, new CheckBox());
table.setText(row, 2, k.getAlgorithm());
table.setText(row, 3, elide(k.getEncodedKey()));
table.setText(row, 4, k.getComment());
table.setText(row, 5, FormatUtil.mediumFormat(k.getLastUsedOn()));
table.setText(row, 6, FormatUtil.mediumFormat(k.getStoredOn()));
final FlexCellFormatter fmt = table.getFlexCellFormatter();
fmt.addStyleName(row, 2, "gerrit-SshKeyPanel-EncodedKey");
fmt.addStyleName(row, 1, S_ICON_CELL);
fmt.addStyleName(row, 3, "gerrit-SshKeyPanel-EncodedKey");
for (int c = 2; c <= 6; c++) {
fmt.addStyleName(row, c, S_DATA_CELL);
}
setRowItem(row, k);
}

View File

@@ -482,3 +482,16 @@
overflow: hidden;
font-family: Courier New, Courier, monospace;
}
.gerrit-AccountInfoBlock {
margin-bottom: 10px;
}
.gerrit-AddSshKeyPanel {
margin-top: 10px;
background-color: #d4e9a9;
padding: 5px 5px 5px 5px;
}
.gerrit-AddSshKeyPanel .gwt-Label {
font-weight: bold;
}