Remove Newsfeed Story Inspector and native plugin mechanism
Summary: Changelog: The 'nativeplugins' on Android are no longer supported Reviewed By: jknoxville Differential Revision: D29163281 fbshipit-source-id: fb4032f240fc306608fe57479f3124d4e7a3400f
This commit is contained in:
committed by
Facebook GitHub Bot
parent
244e3a434c
commit
07199323d1
@@ -1,14 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.facebook.flipper.nativeplugins;
|
|
||||||
|
|
||||||
public interface NativePlugin {
|
|
||||||
String getTitle();
|
|
||||||
|
|
||||||
RawNativePlugin asFlipperPlugin();
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.facebook.flipper.nativeplugins;
|
|
||||||
|
|
||||||
import com.facebook.flipper.core.FlipperClient;
|
|
||||||
|
|
||||||
public class NativePluginRegistry {
|
|
||||||
|
|
||||||
private final FlipperClient client;
|
|
||||||
|
|
||||||
public NativePluginRegistry(FlipperClient client) {
|
|
||||||
this.client = client;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void register(final NativePlugin plugin) {
|
|
||||||
client.addPlugin(plugin.asFlipperPlugin());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.facebook.flipper.nativeplugins;
|
|
||||||
|
|
||||||
import com.facebook.flipper.core.FlipperPlugin;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Subclass of {@link FlipperPlugin} for mobile-defined plugins that conform to a template.
|
|
||||||
* Implementations should call {@link #RawNativePlugin(String, String)} to specify which template
|
|
||||||
* will be used. See {@link com.facebook.flipper.nativeplugins.table.TablePlugin} for an example
|
|
||||||
* subclass.
|
|
||||||
*/
|
|
||||||
public abstract class RawNativePlugin implements FlipperPlugin {
|
|
||||||
private final String pluginType;
|
|
||||||
private final String id;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call super() inside subclass constructors to provide the template name and id of the concrete
|
|
||||||
* plugin instance.
|
|
||||||
*
|
|
||||||
* @param pluginType This needs to correspond to a plugin template defined in Flipper.
|
|
||||||
* @param id This will uniquely
|
|
||||||
*/
|
|
||||||
public RawNativePlugin(final String pluginType, final String id) {
|
|
||||||
this.pluginType = pluginType;
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final String getId() {
|
|
||||||
return "_nativeplugin_" + pluginType + "_" + id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.facebook.flipper.nativeplugins.components;
|
|
||||||
|
|
||||||
import com.facebook.flipper.core.FlipperObject;
|
|
||||||
|
|
||||||
public class JsonSection implements UISection {
|
|
||||||
private final String title;
|
|
||||||
private final FlipperObject content;
|
|
||||||
|
|
||||||
public JsonSection(String title, FlipperObject content) {
|
|
||||||
this.title = title;
|
|
||||||
this.content = content;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FlipperObject serialize() {
|
|
||||||
return new FlipperObject.Builder()
|
|
||||||
.put("title", title)
|
|
||||||
.put("type", "json")
|
|
||||||
.put("content", content)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.facebook.flipper.nativeplugins.components;
|
|
||||||
|
|
||||||
import com.facebook.flipper.core.FlipperArray;
|
|
||||||
|
|
||||||
public class Sidebar {
|
|
||||||
|
|
||||||
private final FlipperArray.Builder sections = new FlipperArray.Builder();
|
|
||||||
|
|
||||||
public Sidebar addSection(UISection section) {
|
|
||||||
sections.put(section.serialize());
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FlipperArray serialize() {
|
|
||||||
return sections.build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.facebook.flipper.nativeplugins.components;
|
|
||||||
|
|
||||||
import com.facebook.flipper.core.FlipperArray;
|
|
||||||
import com.facebook.flipper.core.FlipperObject;
|
|
||||||
|
|
||||||
public class ToolbarSection implements UISection {
|
|
||||||
private FlipperArray.Builder items = new FlipperArray.Builder();
|
|
||||||
|
|
||||||
public ToolbarSection addLink(String label, String destination) {
|
|
||||||
items.put(
|
|
||||||
new FlipperObject.Builder()
|
|
||||||
.put("type", "link")
|
|
||||||
.put("label", label)
|
|
||||||
.put("destination", destination));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FlipperObject serialize() {
|
|
||||||
return new FlipperObject.Builder().put("type", "toolbar").put("items", items.build()).build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.facebook.flipper.nativeplugins.components;
|
|
||||||
|
|
||||||
import com.facebook.flipper.core.FlipperObject;
|
|
||||||
|
|
||||||
interface UISection {
|
|
||||||
|
|
||||||
FlipperObject serialize();
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.facebook.flipper.nativeplugins.table;
|
|
||||||
|
|
||||||
public class Column {
|
|
||||||
public final String id;
|
|
||||||
final String displayName;
|
|
||||||
final String displayWidth;
|
|
||||||
final boolean showByDefault;
|
|
||||||
final boolean isFilterable;
|
|
||||||
|
|
||||||
Column(
|
|
||||||
String id,
|
|
||||||
String displayName,
|
|
||||||
String displayWidth,
|
|
||||||
boolean showByDefault,
|
|
||||||
boolean isFilterable) {
|
|
||||||
if (id == null) {
|
|
||||||
throw new IllegalArgumentException("id must not be null");
|
|
||||||
}
|
|
||||||
if (displayName == null) {
|
|
||||||
throw new IllegalArgumentException("displayName must not be null");
|
|
||||||
}
|
|
||||||
this.id = id;
|
|
||||||
this.displayName = displayName;
|
|
||||||
this.displayWidth = displayWidth;
|
|
||||||
this.showByDefault = showByDefault;
|
|
||||||
this.isFilterable = isFilterable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Builder {
|
|
||||||
private final String id;
|
|
||||||
private String displayName;
|
|
||||||
private String displayWidth;
|
|
||||||
private boolean showByDefault = true;
|
|
||||||
private boolean isFilterable = false;
|
|
||||||
|
|
||||||
public Builder(String id) {
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder displayName(String displayName) {
|
|
||||||
this.displayName = displayName;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder displayWidthPx(int displayWidth) {
|
|
||||||
this.displayWidth = Integer.toString(displayWidth);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder displayWidthPercent(int displayWidth) {
|
|
||||||
this.displayWidth = Integer.toString(displayWidth) + "%";
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder showByDefault(boolean showByDefault) {
|
|
||||||
this.showByDefault = showByDefault;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder isFilterable(boolean isFilterable) {
|
|
||||||
this.isFilterable = isFilterable;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Column build() {
|
|
||||||
return new Column(id, displayName, displayWidth, showByDefault, isFilterable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.facebook.flipper.nativeplugins.table;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public interface QueryableTableRowProvider {
|
|
||||||
|
|
||||||
TableQueryResult getQueryResults(String query);
|
|
||||||
|
|
||||||
class TableQueryResult {
|
|
||||||
final TableMetadata metadata;
|
|
||||||
final List<? extends TableRow> results;
|
|
||||||
|
|
||||||
public TableQueryResult(final TableMetadata metadata, final List<? extends TableRow> results) {
|
|
||||||
this.metadata = metadata;
|
|
||||||
this.results = results;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.facebook.flipper.nativeplugins.table;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import com.facebook.flipper.core.FlipperArray;
|
|
||||||
import com.facebook.flipper.core.FlipperObject;
|
|
||||||
import com.facebook.flipper.nativeplugins.components.ToolbarSection;
|
|
||||||
|
|
||||||
public class TableMetadata {
|
|
||||||
|
|
||||||
final Column[] mColumns;
|
|
||||||
final QueryableTableRowProvider mResponder;
|
|
||||||
final ToolbarSection mTopToolbar;
|
|
||||||
final ToolbarSection mBottomToolbar;
|
|
||||||
|
|
||||||
FlipperObject serialize() {
|
|
||||||
final FlipperObject.Builder columns = new FlipperObject.Builder();
|
|
||||||
final FlipperObject.Builder columnSizes = new FlipperObject.Builder();
|
|
||||||
final FlipperArray.Builder columnOrder = new FlipperArray.Builder();
|
|
||||||
final FlipperArray.Builder filterableColumns = new FlipperArray.Builder();
|
|
||||||
for (Column c : mColumns) {
|
|
||||||
columns.put(c.id, new FlipperObject.Builder().put("value", c.displayName).build());
|
|
||||||
columnSizes.put(c.id, c.displayWidth);
|
|
||||||
columnOrder.put(new FlipperObject.Builder().put("key", c.id).put("visible", c.showByDefault));
|
|
||||||
if (c.isFilterable) {
|
|
||||||
filterableColumns.put(c.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new FlipperObject.Builder()
|
|
||||||
.put("columns", columns.build())
|
|
||||||
.put("columnSizes", columnSizes.build())
|
|
||||||
.put("columnOrder", columnOrder.build())
|
|
||||||
.put("filterableColumns", filterableColumns.build())
|
|
||||||
.put("topToolbar", mTopToolbar != null ? mTopToolbar.serialize() : null)
|
|
||||||
.put("bottomToolbar", mBottomToolbar != null ? mBottomToolbar.serialize() : null)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
private TableMetadata(
|
|
||||||
@Nullable Column[] columns,
|
|
||||||
@Nullable QueryableTableRowProvider queryResponder,
|
|
||||||
@Nullable ToolbarSection topToolbar,
|
|
||||||
@Nullable ToolbarSection bottomToolbar) {
|
|
||||||
this.mColumns = columns == null ? new Column[] {} : columns;
|
|
||||||
this.mResponder = queryResponder;
|
|
||||||
this.mTopToolbar = topToolbar;
|
|
||||||
this.mBottomToolbar = bottomToolbar;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Builder {
|
|
||||||
private Column[] columns;
|
|
||||||
private QueryableTableRowProvider queryResponder;
|
|
||||||
private ToolbarSection topToolbar;
|
|
||||||
private ToolbarSection bottomToolbar;
|
|
||||||
|
|
||||||
public Builder columns(Column... columns) {
|
|
||||||
this.columns = columns;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder queryResponder(QueryableTableRowProvider responder) {
|
|
||||||
this.queryResponder = responder;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder topToolbar(ToolbarSection bar) {
|
|
||||||
this.topToolbar = bar;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder bottomToolbar(ToolbarSection bar) {
|
|
||||||
this.bottomToolbar = bar;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TableMetadata build() {
|
|
||||||
return new TableMetadata(columns, queryResponder, topToolbar, bottomToolbar);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.facebook.flipper.nativeplugins.table;
|
|
||||||
|
|
||||||
import com.facebook.flipper.core.FlipperConnection;
|
|
||||||
import com.facebook.flipper.nativeplugins.NativePlugin;
|
|
||||||
import com.facebook.flipper.nativeplugins.RawNativePlugin;
|
|
||||||
|
|
||||||
public abstract class TablePlugin implements NativePlugin {
|
|
||||||
|
|
||||||
public abstract TableMetadata getMetadata();
|
|
||||||
|
|
||||||
public void onConnect(TableRowDisplay display) {}
|
|
||||||
|
|
||||||
public void onDisconnect() {};
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final RawNativePlugin asFlipperPlugin() {
|
|
||||||
return new RawNativePlugin("Table", getTitle()) {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onConnect(final FlipperConnection connection) throws Exception {
|
|
||||||
final TableRowDisplay display = new TableRowDisplayImpl(connection, TablePlugin.this);
|
|
||||||
TablePlugin.this.onConnect(display);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDisconnect() throws Exception {
|
|
||||||
TablePlugin.this.onDisconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean runInBackground() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.facebook.flipper.nativeplugins.table;
|
|
||||||
|
|
||||||
import com.facebook.flipper.core.FlipperObject;
|
|
||||||
import com.facebook.flipper.nativeplugins.components.Sidebar;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public abstract class TableRow {
|
|
||||||
public interface Value {
|
|
||||||
FlipperObject serialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class StringValue implements Value {
|
|
||||||
private String val;
|
|
||||||
|
|
||||||
public StringValue(String s) {
|
|
||||||
this.val = s;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FlipperObject serialize() {
|
|
||||||
return new FlipperObject.Builder().put("type", "string").put("value", val).build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class IntValue implements Value {
|
|
||||||
private int val;
|
|
||||||
|
|
||||||
public IntValue(int i) {
|
|
||||||
this.val = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FlipperObject serialize() {
|
|
||||||
return new FlipperObject.Builder().put("type", "int").put("value", val).build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class BooleanValue implements Value {
|
|
||||||
private boolean val;
|
|
||||||
|
|
||||||
public BooleanValue(boolean i) {
|
|
||||||
this.val = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FlipperObject serialize() {
|
|
||||||
return new FlipperObject.Builder().put("type", "boolean").put("value", val).build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class TimeValue implements Value {
|
|
||||||
private long millis;
|
|
||||||
|
|
||||||
public TimeValue(long millis) {
|
|
||||||
this.millis = millis;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FlipperObject serialize() {
|
|
||||||
return new FlipperObject.Builder().put("type", "time").put("value", millis).build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class DurationValue implements Value {
|
|
||||||
private long millis;
|
|
||||||
|
|
||||||
public DurationValue(long millis) {
|
|
||||||
this.millis = millis;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FlipperObject serialize() {
|
|
||||||
return new FlipperObject.Builder().put("type", "duration").put("value", millis).build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final String id;
|
|
||||||
final Map<Column, ? extends Value> values;
|
|
||||||
final Sidebar sidebar;
|
|
||||||
|
|
||||||
public TableRow(String id, Map<Column, ? extends Value> values, Sidebar sidebar) {
|
|
||||||
this.id = id;
|
|
||||||
this.values = values;
|
|
||||||
this.sidebar = sidebar;
|
|
||||||
}
|
|
||||||
|
|
||||||
final FlipperObject serialize() {
|
|
||||||
FlipperObject.Builder columnsObject = new FlipperObject.Builder();
|
|
||||||
for (Map.Entry<Column, ? extends Value> e : values.entrySet()) {
|
|
||||||
columnsObject.put(e.getKey().id, e.getValue().serialize());
|
|
||||||
}
|
|
||||||
columnsObject.put("id", id);
|
|
||||||
return new FlipperObject.Builder()
|
|
||||||
.put("columns", columnsObject.build())
|
|
||||||
.put("sidebar", sidebar != null ? sidebar.serialize() : null)
|
|
||||||
.put("id", id)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (o == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (getClass() != o.getClass()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return serialize().equals(((TableRow) o).serialize());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.facebook.flipper.nativeplugins.table;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public interface TableRowDisplay {
|
|
||||||
void updateRow(TableRow row, TableMetadata tableMetadata);
|
|
||||||
|
|
||||||
void updateRows(List<? extends TableRow> rows, TableMetadata tableMetadata);
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.facebook.flipper.nativeplugins.table;
|
|
||||||
|
|
||||||
import com.facebook.flipper.core.FlipperArray;
|
|
||||||
import com.facebook.flipper.core.FlipperConnection;
|
|
||||||
import com.facebook.flipper.core.FlipperObject;
|
|
||||||
import com.facebook.flipper.core.FlipperReceiver;
|
|
||||||
import com.facebook.flipper.core.FlipperResponder;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class TableRowDisplayImpl implements TableRowDisplay {
|
|
||||||
|
|
||||||
private final FlipperConnection mConnection;
|
|
||||||
|
|
||||||
TableRowDisplayImpl(FlipperConnection connection, final TablePlugin subscriber) {
|
|
||||||
this.mConnection = connection;
|
|
||||||
connection.receive(
|
|
||||||
"getMetadata",
|
|
||||||
new FlipperReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onReceive(FlipperObject params, FlipperResponder responder) throws Exception {
|
|
||||||
final FlipperObject metadata = subscriber.getMetadata().serialize();
|
|
||||||
responder.success(metadata);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void updateRow(TableRow row, TableMetadata tableMetadata) {
|
|
||||||
final FlipperArray.Builder array = new FlipperArray.Builder();
|
|
||||||
array.put(row.serialize());
|
|
||||||
this.mConnection.send("updateRows", array.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void updateRows(List<? extends TableRow> rows, TableMetadata tableMetadata) {
|
|
||||||
final FlipperArray.Builder array = new FlipperArray.Builder();
|
|
||||||
for (TableRow r : rows) {
|
|
||||||
array.put(r.serialize());
|
|
||||||
}
|
|
||||||
this.mConnection.send("updateRows", array.build());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.facebook.flipper.nativeplugins.table;
|
|
||||||
|
|
||||||
public class MockTablePlugin extends TablePlugin {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TableMetadata getMetadata() {
|
|
||||||
return new TableMetadata.Builder().columns().build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTitle() {
|
|
||||||
return "Mock Table Plugin";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.facebook.flipper.nativeplugins.table;
|
|
||||||
|
|
||||||
import com.facebook.flipper.nativeplugins.components.Sidebar;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class MockTableRow extends TableRow {
|
|
||||||
public MockTableRow(String id, Map<Column, ? extends Value> values, Sidebar sidebar) {
|
|
||||||
super(id, values, sidebar);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.facebook.flipper.nativeplugins.table;
|
|
||||||
|
|
||||||
public class TableMetadataTestUtils {
|
|
||||||
|
|
||||||
public static Column[] getColumns(TableMetadata tableMetadata) {
|
|
||||||
return tableMetadata.mColumns;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static QueryableTableRowProvider getQueryResponder(TableMetadata tableMetadata) {
|
|
||||||
return tableMetadata.mResponder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.facebook.flipper.nativeplugins.table;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
import com.facebook.flipper.core.FlipperArray;
|
|
||||||
import com.facebook.flipper.core.FlipperObject;
|
|
||||||
import com.facebook.flipper.nativeplugins.components.Sidebar;
|
|
||||||
import com.facebook.flipper.testing.FlipperConnectionMock;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.robolectric.RobolectricTestRunner;
|
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner.class)
|
|
||||||
public class TableRowDisplayImplTest {
|
|
||||||
|
|
||||||
FlipperConnectionMock mConnection;
|
|
||||||
MockTablePlugin mockTablePlugin;
|
|
||||||
|
|
||||||
static final Column NAME_COLUMN =
|
|
||||||
new Column.Builder("name")
|
|
||||||
.displayName("Name")
|
|
||||||
.displayWidthPercent(90)
|
|
||||||
.isFilterable(true)
|
|
||||||
.showByDefault(false)
|
|
||||||
.build();
|
|
||||||
static final Column AGE_COLUMN =
|
|
||||||
new Column.Builder("age")
|
|
||||||
.displayName("Age")
|
|
||||||
.displayWidthPercent(50)
|
|
||||||
.isFilterable(false)
|
|
||||||
.showByDefault(true)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setup() {
|
|
||||||
mConnection = new FlipperConnectionMock();
|
|
||||||
mockTablePlugin = new MockTablePlugin();
|
|
||||||
}
|
|
||||||
|
|
||||||
private TableRow row(String id, String name, int age) {
|
|
||||||
Map<Column, TableRow.Value> map1 = new HashMap<>();
|
|
||||||
map1.put(NAME_COLUMN, new TableRow.StringValue(name));
|
|
||||||
map1.put(AGE_COLUMN, new TableRow.IntValue(age));
|
|
||||||
return new MockTableRow(id, map1, new Sidebar());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testUpdateRow() {
|
|
||||||
TableRowDisplay display = new TableRowDisplayImpl(mConnection, mockTablePlugin);
|
|
||||||
display.updateRow(row("row1", "santa", 55), null);
|
|
||||||
|
|
||||||
assertEquals(1, mConnection.sent.get("updateRows").size());
|
|
||||||
FlipperArray rowArray = (FlipperArray) mConnection.sent.get("updateRows").get(0);
|
|
||||||
assertEquals(1, rowArray.length());
|
|
||||||
FlipperObject updatedRow = rowArray.getObject(0);
|
|
||||||
assertEquals(serializedRow("row1", "santa", 55), updatedRow);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testUpdateRows() {
|
|
||||||
TableRowDisplay display = new TableRowDisplayImpl(mConnection, mockTablePlugin);
|
|
||||||
List<TableRow> rows = new ArrayList<>();
|
|
||||||
rows.add(row("row1", "santa", 55));
|
|
||||||
rows.add(row("row2", "elf", 15));
|
|
||||||
display.updateRows(rows, null);
|
|
||||||
|
|
||||||
assertEquals(1, mConnection.sent.get("updateRows").size());
|
|
||||||
FlipperArray rowArray = (FlipperArray) mConnection.sent.get("updateRows").get(0);
|
|
||||||
assertEquals(2, rowArray.length());
|
|
||||||
assertEquals(serializedRow("row1", "santa", 55), rowArray.getObject(0));
|
|
||||||
assertEquals(serializedRow("row2", "elf", 15), rowArray.getObject(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
private FlipperObject serializedRow(String id, String name, int age) {
|
|
||||||
return new FlipperObject(
|
|
||||||
"{\"columns\":{\"name\":{\"type\":\"string\",\"value\":\""
|
|
||||||
+ name
|
|
||||||
+ "\"},\"id\":\""
|
|
||||||
+ id
|
|
||||||
+ "\",\"age\":{\"type\":\"int\",\"value\":"
|
|
||||||
+ age
|
|
||||||
+ "}},\"sidebar\":[],\"id\":\""
|
|
||||||
+ id
|
|
||||||
+ "\"}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -18,7 +18,6 @@ import {reportPluginFailures} from './utils/metrics';
|
|||||||
import {notNull} from './utils/typeUtils';
|
import {notNull} from './utils/typeUtils';
|
||||||
import {default as isProduction} from './utils/isProduction';
|
import {default as isProduction} from './utils/isProduction';
|
||||||
import {registerPlugins} from './reducers/plugins';
|
import {registerPlugins} from './reducers/plugins';
|
||||||
import createTableNativePlugin from './plugins/TableNativePlugin';
|
|
||||||
import {EventEmitter} from 'events';
|
import {EventEmitter} from 'events';
|
||||||
import invariant from 'invariant';
|
import invariant from 'invariant';
|
||||||
import {
|
import {
|
||||||
@@ -241,27 +240,11 @@ export default class Client extends EventEmitter {
|
|||||||
|
|
||||||
// get the supported plugins
|
// get the supported plugins
|
||||||
async loadPlugins(): Promise<Plugins> {
|
async loadPlugins(): Promise<Plugins> {
|
||||||
const plugins = await this.rawCall<{plugins: Plugins}>(
|
const {plugins} = await this.rawCall<{plugins: Plugins}>(
|
||||||
'getPlugins',
|
'getPlugins',
|
||||||
false,
|
false,
|
||||||
).then((data) => data.plugins);
|
);
|
||||||
this.plugins = plugins;
|
this.plugins = plugins;
|
||||||
const nativeplugins = plugins
|
|
||||||
.map((plugin) => /_nativeplugin_([^_]+)_([^_]+)/.exec(plugin))
|
|
||||||
.filter(notNull)
|
|
||||||
.map(([id, type, title]) => {
|
|
||||||
// TODO put this in another component, and make the "types" registerable
|
|
||||||
console.warn(`TableNative plugins are deprecated: ${id}`);
|
|
||||||
switch (type) {
|
|
||||||
case 'Table':
|
|
||||||
return createTableNativePlugin(id, title);
|
|
||||||
default: {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter(Boolean);
|
|
||||||
this.store.dispatch(registerPlugins(nativeplugins as any));
|
|
||||||
return plugins;
|
return plugins;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,505 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*
|
|
||||||
* @format
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {DataInspector} from 'flipper-plugin';
|
|
||||||
import Panel from '../ui/components/Panel';
|
|
||||||
import {colors} from '../ui/components/colors';
|
|
||||||
import styled from '@emotion/styled';
|
|
||||||
import Text from '../ui/components/Text';
|
|
||||||
import {Spacer} from '../ui/components/Toolbar';
|
|
||||||
import Button from '../ui/components/Button';
|
|
||||||
import Select from '../ui/components/Select';
|
|
||||||
import ErrorBlock from '../ui/components/ErrorBlock';
|
|
||||||
import FlexColumn from '../ui/components/FlexColumn';
|
|
||||||
import SearchableTable from '../ui/components/searchable/SearchableTable';
|
|
||||||
import {
|
|
||||||
TableHighlightedRows,
|
|
||||||
TableRows,
|
|
||||||
TableColumnSizes,
|
|
||||||
TableColumns,
|
|
||||||
TableColumnOrderVal,
|
|
||||||
TableBodyRow,
|
|
||||||
} from '../ui/components/table/types';
|
|
||||||
import {DetailSidebar} from 'flipper-plugin';
|
|
||||||
import {FlipperPlugin} from '../plugin';
|
|
||||||
import textContent from '../utils/textContent';
|
|
||||||
import createPaste from '../fb-stubs/createPaste';
|
|
||||||
import {ReactNode} from 'react';
|
|
||||||
import React from 'react';
|
|
||||||
import {KeyboardActions} from '../MenuBar';
|
|
||||||
import {BundledPluginDetails} from 'flipper-plugin-lib';
|
|
||||||
import {Toolbar} from 'flipper-plugin';
|
|
||||||
|
|
||||||
type ID = string;
|
|
||||||
|
|
||||||
type TableMetadata = {
|
|
||||||
topToolbar?: ToolbarSection;
|
|
||||||
bottomToolbar?: ToolbarSection;
|
|
||||||
columns: TableColumns;
|
|
||||||
columnSizes?: TableColumnSizes;
|
|
||||||
columnOrder?: Array<TableColumnOrderVal>;
|
|
||||||
filterableColumns?: Set<string>;
|
|
||||||
};
|
|
||||||
|
|
||||||
type PersistedState = {
|
|
||||||
rows: TableRows;
|
|
||||||
datas: {[key: string]: NumberedRowData};
|
|
||||||
tableMetadata: TableMetadata | null | undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
type State = {
|
|
||||||
selectedIds: Array<ID>;
|
|
||||||
error: string | null | undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
type RowData = {
|
|
||||||
id: string;
|
|
||||||
columns: {[key: string]: any};
|
|
||||||
sidebar?: Array<SidebarSection>;
|
|
||||||
};
|
|
||||||
|
|
||||||
type NumberedRowData = {
|
|
||||||
id: string;
|
|
||||||
columns: {[key: string]: any};
|
|
||||||
sidebar?: Array<SidebarSection>;
|
|
||||||
rowNumber: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
type SidebarSection = JsonSection | ToolbarSection;
|
|
||||||
type JsonSection = {
|
|
||||||
type: 'json';
|
|
||||||
title: string;
|
|
||||||
content: string;
|
|
||||||
};
|
|
||||||
type ToolbarSection = {
|
|
||||||
type: 'toolbar';
|
|
||||||
items: Array<ToolbarItem>;
|
|
||||||
};
|
|
||||||
type ToolbarItem =
|
|
||||||
| {type: 'link'; destination: string; label: string}
|
|
||||||
| {
|
|
||||||
type: 'input';
|
|
||||||
inputType: 'select';
|
|
||||||
id: string;
|
|
||||||
label: string;
|
|
||||||
options: Array<string>;
|
|
||||||
value: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const NonWrappingText = styled(Text)({
|
|
||||||
overflow: 'hidden',
|
|
||||||
textOverflow: 'ellipsis',
|
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
});
|
|
||||||
|
|
||||||
const BooleanValue = styled(NonWrappingText)<{active?: boolean}>((props) => ({
|
|
||||||
'&::before': {
|
|
||||||
content: '""',
|
|
||||||
display: 'inline-block',
|
|
||||||
width: 8,
|
|
||||||
height: 8,
|
|
||||||
borderRadius: 4,
|
|
||||||
backgroundColor: props.active ? colors.green : colors.red,
|
|
||||||
marginRight: 5,
|
|
||||||
marginTop: 1,
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const Label = styled.span({
|
|
||||||
fontSize: 12,
|
|
||||||
color: '#90949c',
|
|
||||||
fontWeight: 'bold',
|
|
||||||
textTransform: 'uppercase',
|
|
||||||
marginLeft: 5,
|
|
||||||
marginRight: 12,
|
|
||||||
});
|
|
||||||
|
|
||||||
function renderValue({type, value}: {type: string; value: any}) {
|
|
||||||
switch (type) {
|
|
||||||
case 'boolean':
|
|
||||||
return (
|
|
||||||
<BooleanValue code active={value}>
|
|
||||||
{value.toString()}
|
|
||||||
</BooleanValue>
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildRow(
|
|
||||||
rowData: RowData,
|
|
||||||
previousRowData: RowData | null | undefined,
|
|
||||||
): TableBodyRow {
|
|
||||||
if (!rowData.columns) {
|
|
||||||
throw new Error('defaultBuildRow used with incorrect data format.');
|
|
||||||
}
|
|
||||||
const oldColumns =
|
|
||||||
previousRowData && previousRowData.columns
|
|
||||||
? Object.keys(previousRowData.columns).reduce(
|
|
||||||
(map: {[key: string]: {value: any; isFilterable: boolean}}, key) => {
|
|
||||||
if (key !== 'id') {
|
|
||||||
let value = null;
|
|
||||||
if (previousRowData && previousRowData.columns) {
|
|
||||||
value = previousRowData.columns[key].value;
|
|
||||||
}
|
|
||||||
|
|
||||||
map[key] = {
|
|
||||||
value,
|
|
||||||
isFilterable: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
},
|
|
||||||
{},
|
|
||||||
)
|
|
||||||
: {};
|
|
||||||
const columns = Object.keys(rowData.columns).reduce((map, key) => {
|
|
||||||
if (rowData.columns && key !== 'id') {
|
|
||||||
const renderedValue = renderValue(rowData.columns[key]);
|
|
||||||
map[key] = {
|
|
||||||
value: renderedValue,
|
|
||||||
isFilterable: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}, oldColumns);
|
|
||||||
return {
|
|
||||||
columns,
|
|
||||||
key: rowData.id,
|
|
||||||
copyText: () => JSON.stringify(rowData),
|
|
||||||
filterValue: rowData.id,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderToolbar(section: ToolbarSection) {
|
|
||||||
const toolbarComponents = section.items.map((item, index) => {
|
|
||||||
switch (item.type) {
|
|
||||||
case 'link':
|
|
||||||
return (
|
|
||||||
<Button href={item.destination} key={index + 1}>
|
|
||||||
{item.label}
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
case 'input':
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Label>{item.label}</Label>
|
|
||||||
<Select
|
|
||||||
options={item.options.reduce(
|
|
||||||
(obj: {[key: string]: string}, item) => {
|
|
||||||
obj[item] = item;
|
|
||||||
return obj;
|
|
||||||
},
|
|
||||||
{},
|
|
||||||
)}
|
|
||||||
selected={item.value}
|
|
||||||
onChange={() => {}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return (
|
|
||||||
<Toolbar key="toolbar">
|
|
||||||
<Spacer key={0} />
|
|
||||||
{toolbarComponents}
|
|
||||||
</Toolbar>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderSidebarForRow(rowData: RowData): ReactNode {
|
|
||||||
if (!rowData.sidebar) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (!Array.isArray(rowData.sidebar)) {
|
|
||||||
throw new Error('typeof rowData.sidebar is not array as expected: ');
|
|
||||||
}
|
|
||||||
return rowData.sidebar.map(renderSidebarSection);
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderSidebarSection(
|
|
||||||
section: SidebarSection,
|
|
||||||
index: number,
|
|
||||||
): ReactNode {
|
|
||||||
switch (section.type) {
|
|
||||||
case 'json':
|
|
||||||
return (
|
|
||||||
<Panel floating={false} heading={section.title} key={index}>
|
|
||||||
<DataInspector data={section.content} expandRoot />
|
|
||||||
</Panel>
|
|
||||||
);
|
|
||||||
case 'toolbar':
|
|
||||||
return renderToolbar(section);
|
|
||||||
default:
|
|
||||||
return (
|
|
||||||
<Panel floating={false} heading={'Details'} key={index}>
|
|
||||||
<DataInspector data={section} expandRoot />
|
|
||||||
</Panel>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type IncomingMessage =
|
|
||||||
| {method: 'updateRows'; data: Array<RowData>}
|
|
||||||
| {method: 'clearTable'};
|
|
||||||
|
|
||||||
export default function createTableNativePlugin(id: string, title: string) {
|
|
||||||
return class extends FlipperPlugin<State, any, PersistedState> {
|
|
||||||
static keyboardActions: KeyboardActions = ['clear', 'createPaste'];
|
|
||||||
static id = id || '';
|
|
||||||
static title = title || '';
|
|
||||||
|
|
||||||
static details: BundledPluginDetails = {
|
|
||||||
id,
|
|
||||||
title,
|
|
||||||
icon: 'apps',
|
|
||||||
name: id,
|
|
||||||
pluginType: 'client',
|
|
||||||
// all hmm...
|
|
||||||
specVersion: 1,
|
|
||||||
version: 'auto',
|
|
||||||
source: '',
|
|
||||||
main: '',
|
|
||||||
isBundled: true,
|
|
||||||
isActivatable: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
static defaultPersistedState: PersistedState = {
|
|
||||||
rows: [],
|
|
||||||
datas: {},
|
|
||||||
tableMetadata: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
static typedPersistedStateReducer = (
|
|
||||||
persistedState: PersistedState,
|
|
||||||
message: IncomingMessage,
|
|
||||||
): Partial<PersistedState> => {
|
|
||||||
if (message.method === 'updateRows') {
|
|
||||||
const newRows = [];
|
|
||||||
const newData: {[key: string]: NumberedRowData} = {};
|
|
||||||
|
|
||||||
for (const rowData of message.data.reverse()) {
|
|
||||||
if (rowData.id == null) {
|
|
||||||
throw new Error(
|
|
||||||
`updateRows: row is missing id: ${JSON.stringify(rowData)}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const previousRowData: NumberedRowData | null | undefined =
|
|
||||||
persistedState.datas[rowData.id];
|
|
||||||
const newRow: TableBodyRow = buildRow(rowData, previousRowData);
|
|
||||||
if (persistedState.datas[rowData.id] == null) {
|
|
||||||
newData[rowData.id] = {
|
|
||||||
...rowData,
|
|
||||||
rowNumber: persistedState.rows.length + newRows.length,
|
|
||||||
};
|
|
||||||
newRows.push(newRow);
|
|
||||||
} else {
|
|
||||||
persistedState.rows = persistedState.rows
|
|
||||||
.slice(0, persistedState.datas[rowData.id].rowNumber)
|
|
||||||
.concat(
|
|
||||||
[newRow],
|
|
||||||
persistedState.rows.slice(
|
|
||||||
persistedState.datas[rowData.id].rowNumber + 1,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...persistedState,
|
|
||||||
datas: {...persistedState.datas, ...newData},
|
|
||||||
rows: [...persistedState.rows, ...newRows],
|
|
||||||
};
|
|
||||||
} else if (message.method === 'clearTable') {
|
|
||||||
return {
|
|
||||||
...persistedState,
|
|
||||||
rows: [],
|
|
||||||
datas: {},
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static persistedStateReducer(
|
|
||||||
persistedState: PersistedState,
|
|
||||||
method: string,
|
|
||||||
data: Array<RowData> | undefined,
|
|
||||||
): Partial<PersistedState> {
|
|
||||||
const methodEnum = method as 'updateRows' | 'clearTable';
|
|
||||||
const message: IncomingMessage =
|
|
||||||
methodEnum === 'updateRows'
|
|
||||||
? {
|
|
||||||
method: methodEnum,
|
|
||||||
data: data || [],
|
|
||||||
}
|
|
||||||
: {method: methodEnum};
|
|
||||||
return this.typedPersistedStateReducer(persistedState, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
state = {
|
|
||||||
selectedIds: [] as Array<ID>,
|
|
||||||
error: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
init() {
|
|
||||||
this.getTableMetadata();
|
|
||||||
}
|
|
||||||
|
|
||||||
getTableMetadata = () => {
|
|
||||||
if (!this.props.persistedState.tableMetadata) {
|
|
||||||
if (!this.client.isConnected) {
|
|
||||||
this.setState({error: 'Application disconnected'});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.client
|
|
||||||
.call('getMetadata')
|
|
||||||
.then((metadata) => {
|
|
||||||
this.props.setPersistedState({
|
|
||||||
tableMetadata: {
|
|
||||||
...metadata,
|
|
||||||
filterableColumns: new Set(metadata.filterableColumns),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((e) => this.setState({error: e}));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
onKeyboardAction = (action: string) => {
|
|
||||||
if (action === 'clear') {
|
|
||||||
this.clear();
|
|
||||||
} else if (action === 'createPaste') {
|
|
||||||
this.createPaste();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
clear = () => {
|
|
||||||
this.props.setPersistedState({
|
|
||||||
rows: [],
|
|
||||||
datas: {},
|
|
||||||
});
|
|
||||||
this.setState({
|
|
||||||
selectedIds: [],
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
createPaste = () => {
|
|
||||||
if (!this.props.persistedState.tableMetadata) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let paste = '';
|
|
||||||
const mapFn = (row: TableBodyRow) =>
|
|
||||||
(
|
|
||||||
(this.props.persistedState.tableMetadata &&
|
|
||||||
Object.keys(this.props.persistedState.tableMetadata.columns)) ||
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
.map((key) => textContent(row.columns[key].value))
|
|
||||||
.join('\t');
|
|
||||||
|
|
||||||
if (this.state.selectedIds.length > 0) {
|
|
||||||
// create paste from selection
|
|
||||||
paste = this.props.persistedState.rows
|
|
||||||
.filter((row) => this.state.selectedIds.indexOf(row.key) > -1)
|
|
||||||
.map(mapFn)
|
|
||||||
.join('\n');
|
|
||||||
} else {
|
|
||||||
// create paste with all rows
|
|
||||||
paste = this.props.persistedState.rows.map(mapFn).join('\n');
|
|
||||||
}
|
|
||||||
createPaste(paste);
|
|
||||||
};
|
|
||||||
|
|
||||||
onRowHighlighted = (keys: TableHighlightedRows) => {
|
|
||||||
this.setState({
|
|
||||||
selectedIds: keys,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// We don't necessarily have the table metadata at the time when buildRow
|
|
||||||
// is being used. This includes presentation layer info like which
|
|
||||||
// columns should be filterable. This does a pass over the built rows and
|
|
||||||
// applies that presentation layer information.
|
|
||||||
applyMetadataToRows(rows: TableRows): TableRows {
|
|
||||||
if (!this.props.persistedState.tableMetadata) {
|
|
||||||
console.error(
|
|
||||||
'applyMetadataToRows called without tableMetadata present',
|
|
||||||
);
|
|
||||||
return rows;
|
|
||||||
}
|
|
||||||
return rows.map((r) => {
|
|
||||||
return {
|
|
||||||
...r,
|
|
||||||
columns: Object.keys(r.columns).reduce((map, columnName) => {
|
|
||||||
map[columnName].isFilterable =
|
|
||||||
this.props.persistedState.tableMetadata &&
|
|
||||||
this.props.persistedState.tableMetadata.filterableColumns
|
|
||||||
? this.props.persistedState.tableMetadata.filterableColumns.has(
|
|
||||||
columnName,
|
|
||||||
)
|
|
||||||
: false;
|
|
||||||
return map;
|
|
||||||
}, r.columns),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
renderSidebar = () => {
|
|
||||||
const {selectedIds} = this.state;
|
|
||||||
const {datas} = this.props.persistedState;
|
|
||||||
const selectedId = selectedIds.length !== 1 ? null : selectedIds[0];
|
|
||||||
|
|
||||||
if (selectedId != null) {
|
|
||||||
return renderSidebarForRow(datas[selectedId]);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
if (this.state.error) {
|
|
||||||
return <ErrorBlock error={this.state.error} />;
|
|
||||||
}
|
|
||||||
if (!this.props.persistedState.tableMetadata) {
|
|
||||||
return 'Loading...';
|
|
||||||
}
|
|
||||||
const {topToolbar, bottomToolbar, columns, columnSizes, columnOrder} =
|
|
||||||
this.props.persistedState.tableMetadata;
|
|
||||||
const {rows} = this.props.persistedState;
|
|
||||||
|
|
||||||
const topToolbarComponent = topToolbar ? renderToolbar(topToolbar) : null;
|
|
||||||
const bottomToolbarComponent = bottomToolbar
|
|
||||||
? renderToolbar(bottomToolbar)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FlexColumn grow>
|
|
||||||
{topToolbarComponent}
|
|
||||||
<SearchableTable
|
|
||||||
key={this.constructor.id}
|
|
||||||
rowLineHeight={28}
|
|
||||||
floating={false}
|
|
||||||
multiline
|
|
||||||
columnSizes={columnSizes}
|
|
||||||
columnOrder={columnOrder}
|
|
||||||
columns={columns}
|
|
||||||
onRowHighlighted={this.onRowHighlighted}
|
|
||||||
multiHighlight
|
|
||||||
rows={this.applyMetadataToRows(rows)}
|
|
||||||
stickyBottom
|
|
||||||
actions={<Button onClick={this.clear}>Clear Table</Button>}
|
|
||||||
/>
|
|
||||||
<DetailSidebar>{this.renderSidebar()}</DetailSidebar>
|
|
||||||
{bottomToolbarComponent}
|
|
||||||
</FlexColumn>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user