Skip to content

Commit

Permalink
[popup] Add recently viewed list (#325)
Browse files Browse the repository at this point in the history
## Describe your changes

## Issue ticket number and link
#321 

## Checklist before requesting a review
- [x] I have read and understand the [Contributions
section](https://github.com/tprouvot/Salesforce-Inspector-reloaded#contributions)
- [x] Target branch is releaseCandidate and not master
- [x] I have performed a self-review of my code
- [x] I ran the [unit
tests](https://github.com/tprouvot/Salesforce-Inspector-reloaded#unit-tests)
and my PR does not break any tests
- [x] I documented the changes I've made on the
[CHANGES.md](https://github.com/tprouvot/Salesforce-Inspector-reloaded/blob/master/CHANGES.md)
and followed actual conventions
- [ ] I added a new section on
[how-to.md](https://github.com/tprouvot/Salesforce-Inspector-reloaded/blob/master/docs/how-to.md)
(optional)
  • Loading branch information
tprouvot authored Mar 4, 2024
1 parent 4a7cb2c commit 5ecc29e
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 17 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Version 1.23

- Load recently viewed records on popup [feature 321](https://github.com/tprouvot/Salesforce-Inspector-reloaded/issues/321)
- Open documentation when installing the extension [feature 322](https://github.com/tprouvot/Salesforce-Inspector-reloaded/issues/322)
- Add Query Plan to data export [feature 314](https://github.com/tprouvot/Salesforce-Inspector-reloaded/issues/314)
- Align show-all data 'Type' column with Salesforce's 'Data Type' field [issue 312](https://github.com/tprouvot/Salesforce-Inspector-reloaded/issues/312) by [efcdilascio](https://github.com/efcdilascio)
Expand Down
70 changes: 53 additions & 17 deletions addon/popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -967,7 +967,7 @@ class AllDataBoxSObject extends React.PureComponent {
let {selectedValue, recordIdDetails} = this.state;
return (
h("div", {},
h(AllDataSearch, {ref: "allDataSearch", onDataSelect: this.onDataSelect, sobjectsList, getMatches: this.getMatches, inputSearchDelay: 0, placeholderText: "Record id, id prefix or object name", resultRender: this.resultRender}),
h(AllDataSearch, {ref: "allDataSearch", sfHost, onDataSelect: this.onDataSelect, sobjectsList, getMatches: this.getMatches, inputSearchDelay: 0, placeholderText: "Record id, id prefix or object name", title: "Click to show recent items", resultRender: this.resultRender}),
selectedValue
? h(AllDataSelection, {ref: "allDataSelection", sfHost, showDetailsSupported, selectedValue, linkTarget, recordIdDetails, contextRecordId, isFieldsPresent})
: h("div", {className: "all-data-box-inner empty"}, "No record to display")
Expand Down Expand Up @@ -1604,9 +1604,9 @@ class AllDataSelection extends React.PureComponent {
render() {
let {sfHost, showDetailsSupported, contextRecordId, selectedValue, linkTarget, recordIdDetails, isFieldsPresent} = this.props;
// Show buttons for the available APIs.
let buttons = Array.from(selectedValue.sobject.availableApis);
let buttons = selectedValue.sobject.availableApis ? Array.from(selectedValue.sobject.availableApis) : [];
buttons.sort();
if (buttons.length == 0) {
if (buttons.length == 0 && !selectedValue.sobject.isRecent) {
// If none of the APIs are available, show a button for the regular API, which will partly fail, but still show some useful metadata from the tooling API.
buttons.push("noApi");
}
Expand Down Expand Up @@ -1642,7 +1642,7 @@ class AllDataSelection extends React.PureComponent {
h("span", {}, (selectedValue.recordId) ? " / " + selectedValue.recordId : ""),
)
),
selectedValue.sobject.name.indexOf("__") == -1
selectedValue.sobject.name.indexOf("__") == -1 && selectedValue.sobject.availableApis
? h("tr", {},
h("th", {}, "Doc:"),
h("td", {},
Expand Down Expand Up @@ -1762,6 +1762,7 @@ class AllDataSearch extends React.PureComponent {
this.state = {
queryString: "",
matchingResults: [],
recentItems: [],
queryDelayTimer: null
};
this.onAllDataInput = this.onAllDataInput.bind(this);
Expand Down Expand Up @@ -1815,8 +1816,8 @@ class AllDataSearch extends React.PureComponent {
this.setState({queryDelayTimer});
}
render() {
let {queryString, matchingResults} = this.state;
let {placeholderText, resultRender} = this.props;
let {queryString, matchingResults, recentItems} = this.state;
let {placeholderText, resultRender, sfHost} = this.props;
return (
h("div", {className: "input-with-dropdown"},
h("input", {
Expand All @@ -1832,7 +1833,10 @@ class AllDataSearch extends React.PureComponent {
h(Autocomplete, {
ref: "autoComplete",
updateInput: this.updateAllDataInput,
matchingResults: resultRender(matchingResults, queryString)
matchingResults: resultRender(matchingResults, queryString),
recentItems: resultRender(recentItems, queryString),
queryString,
sfHost
}),
h("svg", {viewBox: "0 0 24 24", onClick: this.onAllDataArrowClick},
h("path", {d: "M3.8 6.5h16.4c.4 0 .8.6.4 1l-8 9.8c-.3.3-.9.3-1.2 0l-8-9.8c-.4-.4-.1-1 .4-1z"})
Expand Down Expand Up @@ -1874,7 +1878,32 @@ class Autocomplete extends React.PureComponent {
this.setState({showResults: true, selectedIndex: 0, scrollToSelectedIndex: this.state.scrollToSelectedIndex + 1});
}
handleFocus() {
this.setState({showResults: true, selectedIndex: 0, scrollToSelectedIndex: this.state.scrollToSelectedIndex + 1});
let {recentItems} = this.props;
sfConn.rest("/services/data/v" + apiVersion + "/query/?q=SELECT+Id,Name,Type+FROM+RecentlyViewed+LIMIT+50").then(res => {
res.records.forEach(recentItem => {
recentItems.push({key: recentItem.Id,
value: {recordId: recentItem.Id, sobject: {keyPrefix: recentItem.Id.slice(0, 3), label: recentItem.Type, name: recentItem.Name, isRecent: true}},
element: [
h("div", {className: "autocomplete-item-main", key: "main"},
recentItem.Name,
),
h("div", {className: "autocomplete-item-sub", key: "sub"},
h(MarkSubstring, {
text: recentItem.Type,
start: -1,
length: 0
}),
" • ",
h(MarkSubstring, {
text: recentItem.Id,
start: -1,
length: 0
})
)
]});
});
this.setState({recentItems, showResults: true, selectedIndex: 0, scrollToSelectedIndex: this.state.scrollToSelectedIndex + 1});
});
}
handleBlur() {
this.setState({showResults: false});
Expand Down Expand Up @@ -1930,9 +1959,14 @@ class Autocomplete extends React.PureComponent {
onResultsMouseUp() {
this.setState({resultsMouseIsDown: false});
}
onResultClick(value) {
this.props.updateInput(value);
this.setState({showResults: false, selectedIndex: 0});
onResultClick(e, value) {
let {sfHost} = this.props;
if (value.sobject.isRecent){
window.open("https://" + sfHost + "/" + value.recordId, "_blank");
} else {
this.props.updateInput(value);
this.setState({showResults: false, selectedIndex: 0});
}
}
onResultMouseEnter(index) {
this.setState({selectedIndex: index, scrollToSelectedIndex: this.state.scrollToSelectedIndex + 1});
Expand Down Expand Up @@ -1965,34 +1999,36 @@ class Autocomplete extends React.PureComponent {
}
}
render() {
let {matchingResults} = this.props;
let {matchingResults, recentItems} = this.props;
let {
showResults,
selectedIndex,
scrollTopIndex,
itemHeight,
resultsMouseIsDown,
resultsMouseIsDown
} = this.state;
// For better performance only render the visible autocomplete items + at least one invisible item above and below (if they exist)
const RENDERED_ITEMS_COUNT = 11;
let firstIndex = 0;
let lastIndex = matchingResults.length - 1;
let autocompleteResults = recentItems.length > 0 ? recentItems : matchingResults;
let lastIndex = autocompleteResults.length - 1;
let firstRenderedIndex = Math.max(0, scrollTopIndex - 2);
let lastRenderedIndex = Math.min(lastIndex, firstRenderedIndex + RENDERED_ITEMS_COUNT);
let topSpace = (firstRenderedIndex - firstIndex) * itemHeight;
let bottomSpace = (lastIndex - lastRenderedIndex) * itemHeight;
let topSelected = (selectedIndex - firstIndex) * itemHeight;

return (
h("div", {className: "autocomplete-container", style: {display: (showResults && matchingResults.length > 0) || resultsMouseIsDown ? "" : "none"}, onMouseDown: this.onResultsMouseDown, onMouseUp: this.onResultsMouseUp},
h("div", {className: "autocomplete-container", style: {display: (showResults && (autocompleteResults.length > 0)) || resultsMouseIsDown ? "" : "none"}, onMouseDown: this.onResultsMouseDown, onMouseUp: this.onResultsMouseUp},
h("div", {className: "autocomplete", onScroll: this.onScroll, ref: "scrollBox"},
h("div", {ref: "selectedItem", style: {position: "absolute", top: topSelected + "px", height: itemHeight + "px"}}),
h("div", {style: {height: topSpace + "px"}}),
matchingResults.slice(firstRenderedIndex, lastRenderedIndex + 1)
autocompleteResults.slice(firstRenderedIndex, lastRenderedIndex + 1)
.map(({key, value, element}, index) =>
h("a", {
key,
className: "autocomplete-item " + (selectedIndex == index + firstRenderedIndex ? "selected" : ""),
onClick: () => this.onResultClick(value),
onClick: (e) => this.onResultClick(e, value),
onMouseEnter: () => this.onResultMouseEnter(index + firstRenderedIndex)
}, element)
),
Expand Down

0 comments on commit 5ecc29e

Please sign in to comment.