Browse Source

Merge pull request #11308 in DESIGN/design from release/11.0 to bugfix/11.0

* commit '2d0539fc358504f8a090abccffe7162372dbaf56':
  REPORT-89260 && REPORT-83847 远程设计下卡顿以及规则异常 代码质量问题,添加注释
  REPORT-89260 && REPORT-83847 远程设计下卡顿以及规则异常 【问题原因】卡顿是因为查询数据库的操作放在了UI线程里;规则异常是因为规则管理器DesensitizationRuleManager中的规则缓存对环境切换时的监听写的有点问题 【改动思路】修复卡顿问题是加SwingWorker;规则异常这个,想了下DesensitizationRuleManager中的规则缓存此前只针对了本地规则变化、远程环境切换,但是没针对远程下的规则变化,而且好像没有太好的思路。现在的做法是直接去掉规则缓存,直接查数据库,套用Config框架的缓存 【review建议】
  REPORT-89167 && REPORT-83493 平台脱敏规则变动时的交互问题 【问题原因】与产品沟通修改成更容易理解的交互还有规则变动时的生效逻辑,大致如下: 1. 规则异常的提示放在数据集"数据脱敏设置"页面中,鼠标悬停"异常"文字时,出现气泡提示,气泡无法点击;"数据脱敏设置"页面中的规则表,表上增加刷新按钮,点击刷新后会刷新当前规则,刷新逻辑: 规则被禁用 ---- 原规则不变 规则被删除 ---- 原规则删除 规则被修改 ---- 原规则B被替换成B* 2. 假如模板中存在A、B平台规则,A规则正常,B规则被修改成了B*规则,此时不管是模板预览、数据集脱敏效果预览等计算相关的逻辑里,生效规则为A和B*。即计算场景里会自动替换被修改的规则 3. 上面的替换是临时替换,保存模板时并不会将B*写入模板xml中。仅在"数据脱敏设置"页面里,用户在点击"异常",弹窗提示出现后,再点击确定 + 保存模板时,才会修改模板xml文件 【改动方案】同上 【review建议】
bugfix/11.0
superman 2 years ago
parent
commit
2757196422
  1. 30
      designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/TableDataPreviewDesensitizeManager.java
  2. 162
      designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/view/setting/TableDataDesensitizationTableModel.java
  3. 7
      designer-base/src/main/resources/com/fr/design/standard/refresh/refresh_normal.svg

30
designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/TableDataPreviewDesensitizeManager.java

@ -2,10 +2,11 @@ package com.fr.design.data.datapane.preview.desensitization;
import com.fr.base.TableData; import com.fr.base.TableData;
import com.fr.data.TableDataSource;
import com.fr.data.desensitize.TableDataDesensitizeManager;
import com.fr.data.desensitize.base.DesensitizationTableData; import com.fr.data.desensitize.base.DesensitizationTableData;
import com.fr.data.desensitize.base.TableDataDesensitizationItem; import com.fr.data.desensitize.base.TableDataDesensitizationItem;
import com.fr.data.desensitize.manage.DesensitizationManager; import com.fr.data.desensitize.manage.DesensitizationManager;
import com.fr.data.desensitize.rule.DesensitizationRuleManager;
import com.fr.data.desensitize.util.DesentizationUtils; import com.fr.data.desensitize.util.DesentizationUtils;
import com.fr.decision.webservice.bean.user.DepartmentPostBean; import com.fr.decision.webservice.bean.user.DepartmentPostBean;
import com.fr.decision.webservice.bean.user.RoleBean; import com.fr.decision.webservice.bean.user.RoleBean;
@ -15,11 +16,14 @@ import com.fr.decision.webservice.v10.user.PositionService;
import com.fr.design.data.DesignTableDataManager; import com.fr.design.data.DesignTableDataManager;
import com.fr.design.data.datapane.preview.PreviewTableModel; import com.fr.design.data.datapane.preview.PreviewTableModel;
import com.fr.design.data.datapane.preview.desensitization.model.DesensitizedPreviewTableModel; import com.fr.design.data.datapane.preview.desensitization.model.DesensitizedPreviewTableModel;
import com.fr.esd.query.StrategicTableData;
import com.fr.general.ComparatorUtils; import com.fr.general.ComparatorUtils;
import com.fr.log.FineLoggerFactory; import com.fr.log.FineLoggerFactory;
import com.fr.stable.StringUtils; import com.fr.stable.StringUtils;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
@ -79,7 +83,7 @@ public class TableDataPreviewDesensitizeManager implements DesensitizationManage
Collection<TableDataDesensitizationItem> desensitizationItems = ((DesensitizationTableData) tableData).getDesensitizationConfig().getDesensitizationItems(); Collection<TableDataDesensitizationItem> desensitizationItems = ((DesensitizationTableData) tableData).getDesensitizationConfig().getDesensitizationItems();
if (DesentizationUtils.isCollectionNotEmpty(desensitizationItems)) { if (DesentizationUtils.isCollectionNotEmpty(desensitizationItems)) {
// 更新规则 // 更新规则
dealWithLastedRules(desensitizationItems); desensitizationItems = TableDataDesensitizeManager.getInstance().dealWithLastedRules(desensitizationItems);
// 对脱敏配置项集合做过滤和排序处理 // 对脱敏配置项集合做过滤和排序处理
List<TableDataDesensitizationItem> items = desensitizationItems.stream() List<TableDataDesensitizationItem> items = desensitizationItems.stream()
.filter(item -> isAvaliableItem4Preview(item, model)) .filter(item -> isAvaliableItem4Preview(item, model))
@ -107,23 +111,17 @@ public class TableDataPreviewDesensitizeManager implements DesensitizationManage
} }
/** /**
* 在预览计算前将Item中的规则替换为最新规则防止平台规则被临时修改 * 通过TableData获取其列名,理论上一定存在缓存值
*/
private void dealWithLastedRules(Collection<TableDataDesensitizationItem> desensitizationItems) {
desensitizationItems.forEach(item -> {
// 获取最新的规则
item.setRule(DesensitizationRuleManager.getInstance().getLastedDesentizationRule(item.getRule()));
});
}
/**
* 通过TableData获取其列名
* *
* @param tableData * @param tableData
* @return * @return
*/ */
public List<String> getColumnNamesByTableData(TableData tableData) { public List<String> getColumnNamesByTableData(TableData tableData) {
return DesignTableDataManager.getColumnNamesByTableData(tableData); TableDataSource editingTableDataSource = DesignTableDataManager.getEditingTableDataSource();
if (editingTableDataSource != null && tableData instanceof StrategicTableData) {
return Arrays.asList(DesignTableDataManager.getSelectedColumnNames(editingTableDataSource, ((StrategicTableData) tableData).getDsName()));
}
return Collections.EMPTY_LIST;
} }
/** /**
@ -185,7 +183,7 @@ public class TableDataPreviewDesensitizeManager implements DesensitizationManage
* @param positionName * @param positionName
* @return * @return
*/ */
public String mergeRoleText(String departmentName, String positionName) { private String mergeRoleText(String departmentName, String positionName) {
return departmentName + positionName; return departmentName + positionName;
} }
@ -196,7 +194,7 @@ public class TableDataPreviewDesensitizeManager implements DesensitizationManage
* @param positionId * @param positionId
* @return * @return
*/ */
public String mergeRoleId(String departmentId, String positionId) { private String mergeRoleId(String departmentId, String positionId) {
return departmentId + CONNECTOR + positionId; return departmentId + CONNECTOR + positionId;
} }

162
designer-base/src/main/java/com/fr/design/data/datapane/preview/desensitization/view/setting/TableDataDesensitizationTableModel.java

@ -9,7 +9,6 @@ import com.fr.design.data.datapane.preview.desensitization.TableDataPreviewDesen
import com.fr.design.data.datapane.preview.desensitization.view.rule.DesensitizationRulePane; import com.fr.design.data.datapane.preview.desensitization.view.rule.DesensitizationRulePane;
import com.fr.design.dialog.BasicDialog; import com.fr.design.dialog.BasicDialog;
import com.fr.design.dialog.DialogActionAdapter; import com.fr.design.dialog.DialogActionAdapter;
import com.fr.design.dialog.FineJOptionPane;
import com.fr.design.gui.ibutton.UIButton; import com.fr.design.gui.ibutton.UIButton;
import com.fr.design.gui.icombobox.UIComboBox; import com.fr.design.gui.icombobox.UIComboBox;
import com.fr.design.gui.icombocheckbox.UIComboCheckBox; import com.fr.design.gui.icombocheckbox.UIComboCheckBox;
@ -21,6 +20,7 @@ import com.fr.design.i18n.Toolkit;
import com.fr.design.layout.FRGUIPaneFactory; import com.fr.design.layout.FRGUIPaneFactory;
import com.fr.design.layout.TableLayout; import com.fr.design.layout.TableLayout;
import com.fr.design.layout.TableLayoutHelper; import com.fr.design.layout.TableLayoutHelper;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.StringUtils; import com.fr.stable.StringUtils;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -29,6 +29,7 @@ import javax.swing.JComponent;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JTable; import javax.swing.JTable;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.event.CellEditorListener; import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeEvent;
import javax.swing.table.TableCellEditor; import javax.swing.table.TableCellEditor;
@ -38,8 +39,11 @@ import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -60,17 +64,15 @@ public class TableDataDesensitizationTableModel extends UITableModelAdapter<Tabl
private static final String COMMA = ","; private static final String COMMA = ",";
private DesensitizationTableData tableData;
/** /**
* 当前数据集的所有列名 * 当前数据集的所有列名
*/ */
private List<String> columnNames; private List<String> columnNames = new ArrayList<>();
/** /**
* key为用户组唯一标识id拼接value为用户组名称 * key为用户组唯一标识id拼接value为用户组名称
*/ */
private Map<String, String> roleMap; private final Map<String, String> roleMap = new LinkedHashMap<>();
private Component parent; private Component parent;
@ -83,12 +85,6 @@ public class TableDataDesensitizationTableModel extends UITableModelAdapter<Tabl
Toolkit.i18nText("Fine-Design_Report_Desensitization_Effected_Roles"), Toolkit.i18nText("Fine-Design_Report_Desensitization_Effected_Roles"),
Toolkit.i18nText("Fine-Design_Report_Desensitization_Rule_Status"), Toolkit.i18nText("Fine-Design_Report_Desensitization_Rule_Status"),
}); });
// 一些数据相关
this.tableData = tableData;
// 获取当前数据集的所有列名
this.columnNames = TableDataPreviewDesensitizeManager.getInstance().getColumnNamesByTableData(tableData);
// 获取当前所有用户组
this.roleMap = TableDataPreviewDesensitizeManager.getInstance().getAllRoles();
this.parent = parent; this.parent = parent;
this.setColumnClass(new Class[]{ this.setColumnClass(new Class[]{
// 列名选择 // 列名选择
@ -102,15 +98,41 @@ public class TableDataDesensitizationTableModel extends UITableModelAdapter<Tabl
// 规则状态 // 规则状态
DesensitizationRuleStatusPane.class DesensitizationRuleStatusPane.class
}); });
ColumnNamesComboBox columnNamesComboBox = new ColumnNamesComboBox();
this.setDefaultEditor(ColumnNamesComboBox.class, new ColumnNamesComboBox()); this.setDefaultEditor(ColumnNamesComboBox.class, columnNamesComboBox);
this.setDefaultEditor(DesensitizationRuleChooser.class, new DesensitizationRuleChooser()); this.setDefaultEditor(DesensitizationRuleChooser.class, new DesensitizationRuleChooser());
this.setDefaultEditor(DesensitizationRuleDescriptionPane.class, new DesensitizationRuleDescriptionPane()); this.setDefaultEditor(DesensitizationRuleDescriptionPane.class, new DesensitizationRuleDescriptionPane());
this.setDefaultEditor(EffectedRolesChooser.class, new EffectedRolesChooser()); EffectedRolesChooser effectedRolesChooser = new EffectedRolesChooser();
this.setDefaultEditor(EffectedRolesChooser.class, effectedRolesChooser);
this.setDefaultEditor(DesensitizationRuleStatusPane.class, new DesensitizationRuleStatusPane()); this.setDefaultEditor(DesensitizationRuleStatusPane.class, new DesensitizationRuleStatusPane());
this.setDefaultRenderer(DesensitizationRuleStatusPane.class, new DesensitizationRuleStatusPane()); this.setDefaultRenderer(DesensitizationRuleStatusPane.class, new DesensitizationRuleStatusPane());
this.createTable().getColumnModel().getColumn(TableSequences.DesensitizationRuleStatus.getNum()).setMaxWidth(60); this.createTable().getColumnModel().getColumn(TableSequences.DesensitizationRuleStatus.getNum()).setMaxWidth(60);
new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
// 获取当前数据集的所有列名
columnNames.addAll(TableDataPreviewDesensitizeManager.getInstance().getColumnNamesByTableData(tableData));
// 获取当前所有用户组
roleMap.putAll(TableDataPreviewDesensitizeManager.getInstance().getAllRoles());
return null;
}
@Override
protected void done() {
try {
// 更新列名选择框和生效用户组选择框
columnNamesComboBox.refresh();
effectedRolesChooser.refresh();
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e, "[Desensitization] get column names or all roles failed for {}", e.getMessage());
}
}
}.execute();
} }
@Override @Override
@ -132,9 +154,7 @@ public class TableDataDesensitizationTableModel extends UITableModelAdapter<Tabl
return matchRoleNamesByIds(desensitizationItem.getRoleIds()); return matchRoleNamesByIds(desensitizationItem.getRoleIds());
case DesensitizationRuleStatus: case DesensitizationRuleStatus:
// 规则状态 // 规则状态
DesensitizationRuleStatus ruleStatus = DesensitizationRuleManager.getInstance().getRuleStatus(desensitizationItem.getRule()); return needMarkRule(desensitizationItem.getRule()) ? StringUtils.EMPTY : Toolkit.i18nText("Fine-Design_Report_Desensitization_Rule_Status_Abnormal");
// 非正常状态需要标记为异常
return ruleStatus == DesensitizationRuleStatus.NORMAL ? StringUtils.EMPTY : Toolkit.i18nText("Fine-Design_Report_Desensitization_Rule_Status_Abnormal");
default: default:
return StringUtils.EMPTY; return StringUtils.EMPTY;
} }
@ -165,7 +185,11 @@ public class TableDataDesensitizationTableModel extends UITableModelAdapter<Tabl
@Override @Override
public UITableEditAction[] createAction() { public UITableEditAction[] createAction() {
return new UITableEditAction[]{new AddDesensitizationAction(), new RemoveDesensitizationAction(parent)}; return new UITableEditAction[]{
new AddDesensitizationAction(),
new RemoveDesensitizationAction(parent),
new RefreshTableAction(),
};
} }
/** /**
@ -207,6 +231,13 @@ public class TableDataDesensitizationTableModel extends UITableModelAdapter<Tabl
}); });
} }
/**
* 查询到数据集列名后更新到下拉框中
*/
public void refresh() {
columnNameComboBox.refreshBoxItems(columnNames);
}
@Override @Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
@ -325,7 +356,7 @@ public class TableDataDesensitizationTableModel extends UITableModelAdapter<Tabl
private UIComboCheckBox rolesCheckBox; private UIComboCheckBox rolesCheckBox;
EffectedRolesChooser() { EffectedRolesChooser() {
this.rolesCheckBox = new UIComboCheckBox(roleMap.values().toArray(), true) { this.rolesCheckBox = new UIComboCheckBox(new String[]{}, true) {
@Override @Override
protected void setLayoutAndAddComponents() { protected void setLayoutAndAddComponents() {
// 使用BorderLayout,否则默认使用的FlowLayout会让整个下拉选框使用最小Size,然后TableCell这边会出现空白 // 使用BorderLayout,否则默认使用的FlowLayout会让整个下拉选框使用最小Size,然后TableCell这边会出现空白
@ -359,6 +390,13 @@ public class TableDataDesensitizationTableModel extends UITableModelAdapter<Tabl
}); });
} }
/**
* 查询到所有用户组后更新到下拉框中
*/
public void refresh() {
rolesCheckBox.refreshCombo(roleMap.values().toArray());
}
@Override @Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
rolesCheckBox.setSelectedValues(generateRolesCheckBoxSelectedValues(getList().get(row))); rolesCheckBox.setSelectedValues(generateRolesCheckBoxSelectedValues(getList().get(row)));
@ -428,44 +466,16 @@ public class TableDataDesensitizationTableModel extends UITableModelAdapter<Tabl
* @param currentItem * @param currentItem
*/ */
public void refreshRuleStatus(TableDataDesensitizationItem currentItem) { public void refreshRuleStatus(TableDataDesensitizationItem currentItem) {
DesensitizationRuleStatus ruleStatus = DesensitizationRuleManager.getInstance().getRuleStatus(currentItem.getRule()); DesensitizationRule rule = currentItem.getRule();
if (ruleStatus == DesensitizationRuleStatus.NORMAL) { if (needMarkRule(rule)) {
// 正常规则时,重置提示Label // 非正常规则,根据规则状态展示不同提示文字
this.ruleStatusLabel.setText(StringUtils.EMPTY); DesensitizationRuleStatus ruleStatus = DesensitizationRuleManager.getInstance().getRuleStatus(rule);
this.ruleStatusLabel.setToolTipText(null);
} else {
this.ruleStatusLabel.setText(Toolkit.i18nText("Fine-Design_Report_Desensitization_Rule_Status_Abnormal")); this.ruleStatusLabel.setText(Toolkit.i18nText("Fine-Design_Report_Desensitization_Rule_Status_Abnormal"));
this.ruleStatusLabel.setToolTipText(ruleStatus.getDescription()); this.ruleStatusLabel.setToolTipText(ruleStatus.getDescription());
} } else {
} // 正常规则,重置提示Label
this.ruleStatusLabel.setText(StringUtils.EMPTY);
/** this.ruleStatusLabel.setToolTipText(null);
* 标签点击后处理原规则
*/
private void ruleStatusLabelClick(TableDataDesensitizationItem currentItem) {
DesensitizationRuleStatus ruleStatus = DesensitizationRuleManager.getInstance().getRuleStatus(currentItem.getRule());
if (ruleStatus == DesensitizationRuleStatus.NORMAL) {
return;
}
// 点击Label后,弹窗提示
int option = FineJOptionPane.showConfirmDialog(
parent,
ruleStatus.getDescription(),
Toolkit.i18nText("Fine-Design_Basic_Tool_Tips"),
FineJOptionPane.OK_CANCEL_OPTION,
FineJOptionPane.INFORMATION_MESSAGE);
if (option == FineJOptionPane.OK_OPTION && table.getSelectedRow() != -1) {
int selectedRow = table.getSelectedRow();
// 点击确定后,根据规则状态对规则做处理
if (ruleStatus == DesensitizationRuleStatus.REMOVED || ruleStatus == DesensitizationRuleStatus.WRONG) {
// 规则被删除或读取错误时,直接删掉
removeRow(selectedRow);
} else if (ruleStatus == DesensitizationRuleStatus.MODIFIED) {
// 规则被修改时,用新规则替换
DesensitizationRule lastedRule = DesensitizationRuleManager.getInstance().getLastedDesentizationRule(currentItem.getRule());
currentItem.setRule(lastedRule);
setRowAt(currentItem, selectedRow);
}
} }
} }
@ -518,6 +528,36 @@ public class TableDataDesensitizationTableModel extends UITableModelAdapter<Tabl
} }
} }
private class RefreshTableAction extends UITableEditAction {
public RefreshTableAction() {
this.setName(Toolkit.i18nText("Fine-Design_Basic_Refresh"));
this.setSmallIcon("/com/fr/design/standard/refresh/refresh", false);
}
@Override
public void actionPerformed(ActionEvent e) {
// 刷新TableData的规则,主要是为了自动替换掉平台中被修改的规则
List<TableDataDesensitizationItem> items = getList();
Iterator<TableDataDesensitizationItem> iterator = items.iterator();
while (iterator.hasNext()) {
TableDataDesensitizationItem item = iterator.next();
DesensitizationRuleStatus ruleStatus = DesensitizationRuleManager.getInstance().getRuleStatus(item.getRule());
if (ruleStatus == DesensitizationRuleStatus.REMOVED) {
// 规则被移除,则删除整条脱敏Item
iterator.remove();
} else {
// 规则被修改、禁用等,更新一下规则
item.setRule(DesensitizationRuleManager.getInstance().getLastedDesentizationRule(item.getRule()));
}
}
fireTableDataChanged();
}
@Override
public void checkEnabled() {}
}
/** /**
* 规则表-列字段编号 * 规则表-列字段编号
*/ */
@ -580,4 +620,16 @@ public class TableDataDesensitizationTableModel extends UITableModelAdapter<Tabl
} }
} }
/**
* 是否需要对异常规则做标记需要满足
* 1. 规则非默认空规则
* 2. 规则本身异常
*
* @return
*/
private boolean needMarkRule(DesensitizationRule rule) {
return !rule.equals(DesensitizationRule.createDefaultEmptyRule()) &&
DesensitizationRuleManager.getInstance().getRuleStatus(rule) != DesensitizationRuleStatus.NORMAL;
}
} }

7
designer-base/src/main/resources/com/fr/design/standard/refresh/refresh_normal.svg

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>icon_刷新_normal</title>
<g id="icon_刷新_normal" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M13.0593807,1 C13.4802225,1 13.8213822,1.34115968 13.8213822,1.76200147 L13.8213822,1.76200147 L13.8213822,5.2534957 C13.8213822,5.67433749 13.4802225,6.01549717 13.0593807,6.01549717 L13.0593807,6.01549717 L9.56788646,6.01549717 C9.14704467,6.01549717 8.80588499,5.67433749 8.80588499,5.2534957 C8.80588499,4.83265391 9.14704467,4.49149423 9.56788646,4.49149423 L9.56788646,4.49149423 L11.169634,4.48959417 C10.8434607,4.27112237 10.4895308,4.09123525 10.1125367,3.95402064 C7.56533579,3.02691531 4.74885407,4.34026231 3.82174874,6.88746326 C2.89464341,9.43466421 4.20799041,12.2511459 6.75519136,13.1782513 C9.30239231,14.1053566 12.118874,12.7920096 13.0459794,10.2448086 C13.1899157,9.84934672 13.6271844,9.64544497 14.0226464,9.78938133 C14.4181083,9.9333177 14.62201,10.3705864 14.4780737,10.7660483 C13.2630956,14.1041731 9.57207646,15.8253236 6.23395166,14.6103456 C2.89582685,13.3953675 1.17467636,9.70434836 2.38965442,6.36622356 C3.60463249,3.02809876 7.29565164,1.30694826 10.6337764,2.52192632 C11.3047108,2.76612647 11.9217951,3.11486447 12.467588,3.55405023 L12.297,3.423 L12.2973792,1.76200147 C12.2973792,1.37622983 12.5840481,1.05741329 12.9559816,1.00695618 Z" id="Combined-Shape" fill="#333334" fill-rule="nonzero"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

Loading…
Cancel
Save