Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Search Injection #320

Merged
merged 23 commits into from Mar 29, 2018
Merged

Conversation

digi0ps
Copy link
Contributor

@digi0ps digi0ps commented Feb 24, 2018

image

Fixes #281

Implemented injecting Memex results along with Google Search results.
I've created a new module in src/search-injection where the content_script resides.
The content script:

  • Matches the URL against a search engine's search URL
  • If the regex produces a match, retrieves the query parameter from the URL
  • Opens a two way connection to the background search script and sends the query
  • The results ( limited to 3 ) are then embedded in a React component and rendered on the page.

TODOs:

  • Make it load everytime
  • Add Show More Results button which takes the user to the overview page.
  • Add toggle in results screen
  • Better UI
  • Maximize/Minimize
  • Remove Results Forever
  • Change positions
  • Update UI as per requirements
  • Show screenshot alongside results
  • Generalise the URL regex matching for all search engines

@blackforestboi
Copy link
Member

blackforestboi commented Feb 24, 2018

Wow great that you got it running so quickly!

I just tested it and it does not work, showing me the error:
screen shot 2018-02-24 at 14 09 42

If i search via the regular search overview it gives me back the results.

@digi0ps
Copy link
Contributor Author

digi0ps commented Feb 24, 2018

Ah snap, that is because of the DOM element which is used to append the results hasn't been loaded on the page yet. ( Temporary fix: Try refreshing the page ) Am working on a proper fix for that. @oliversauter

@blackforestboi
Copy link
Member

Just gave it a test and works wonderfully now!

Will leave some design notes in the issue itself, because now I have the designs for it :)

@digi0ps digi0ps force-pushed the search-injection branch 3 times, most recently from 8f022b3 to 9c37f2e Compare March 11, 2018 16:56
@digi0ps
Copy link
Contributor Author

digi0ps commented Mar 11, 2018

@oliversauter Check it out now. Prepended the css and can change positions. Have some UI tweaks left to do.

@blackforestboi
Copy link
Member

Hey Sriram, thanks for updating your PR!

Just checked on it, still seem the same issue with the loading, and some others I uncovered:

  1. it still shows the unstyled version when loading, resulting in an unsexy glitch.
    This is a screenshot of it: screen shot 2018-03-11 at 22 34 31
  2. when hovering over "See all results" it should show the glove cursor
  3. the very first time clicking "change position of memex" it does not work, only the second time. (could also reproduce it sometimes randomly that the first try does not work)
  4. the settings icon is cut off when results are on the right. screen shot 2018-03-11 at 22 38 50
  5. Change Designs to the mockups of Jean Baptiste. See here.
  6. we should definitely write tests for the major functionalities
  7. when the results are on the right, we can show 5 results.
  8. make sure the title overflows and not breaks into a second line, if it is too long.

@digi0ps digi0ps force-pushed the search-injection branch 10 times, most recently from ccf113f to 237fa10 Compare March 18, 2018 19:53
@@ -18,7 +18,7 @@ export const SEARCH_ENGINES = {
google: {
regex: /(http[s]?:\/\/)?(www.)?google[.\w]+\/search\?.*/,
container: {
above: 'center_col',
above: 'res',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Appending to res makes Memex Container go below ads. Did you see that?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed it again, thanks for the headsup!

Copy link
Member

@poltak poltak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pretty awesome work @digi0ps and @oliversauter ! Code is pretty impressive. I built and tried it out, and my experience using it was really good. Very smooth, and cool how you can move it around.

I would postpone the screenshots for now. In the current index it is very painful to get them as you need to bring in Pouch and fetch the data for each result. In the new index, they are returned with the rest of the search results, so it will just be a matter of rendering them straight in a <img> somewhere once we merge that in

@@ -44,6 +45,13 @@ async function openOverview() {
}
}

const openOverviewURL = url => chrome.tabs.create({ url })
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everywhere you've used the chrome global, replace it with browser for consistency with the rest of codebase. They provide similar APIs, although we're using the web ext polyfill (assigned to browser) as it provides more consistency across browsers and uses Promises for async stuff

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, with the openOverviewURL function, why does it differ from openOptionsURL function in taking an absolute URL rather than just the path? I think you could simplify it a lot by just hardcoding the overview URL in there and having a query arg to append to it. Then content script side just needs to send the query; no need to know what the URL is.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah right. I didn't know why I did that .-.
I will update it!

import SearchInjection from './components/SearchInjection'

const Settings = () => (
<Wrapper>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think <Wrapper> should be needed here. This is a "noop" component; it renders nothing to the real DOM. Only used as a helper to make some JSX code neater. And if you can remove this, this whole module is just const Settings = () => <SearchInjection /> - so probably unnecessary.

Instead what you could do is separate the view parts of SearchInjection (everything in the render() method) into it's own dumb component and just have the other state-interacting methods within a SearchInjectionContainer class (render() would simply just render the dumb component and pass in the needed props). This is the general container-view pattern in React.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oooh. I thought wrapper was for the UI. Great second point.
Will this be good: I do all the state related logic in Settings component and pass it to the SearchInjection component?


const openSettings = () => {
const message = {
action: 'openOptionsURL',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to define these action names somewhere which is imported by both this content script code and the corresponding listener in the background script code. Maybe the search-injection/constants module?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it.

}

toggleDropDown() {
this.setState(state => ({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're using both reducer and object versions of this.setState; it would be good to be consistent with which you use. In the rest of the code base we're trying to use the reducer (function) taking version. Really important to note though, if you use the reducer version like here: the returned value of the callback will be the new state.

this.setState(state => ({ dropdown: !state.dropdown }))

The above will unset all the other state keys, like hideResults, meaing they will be undefined whenever they get accessed via this.state. That may be your intention here though, but it's often a gotcha with this.setState.

The following would ensure dropdown state is updated, and all other state keys are left alone:

this.setState(state => ({ ...state, dropdown: !state.dropdown }))

return null
}
return (
<div
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, good to move this to its own view module. Separate the state interaction and views


// Append content_script.css to the document
const cssFile = browser.extension.getURL('/content_script.css')
appendCss(cssFile)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems a bit odd to have the appendCss logic - which prepends (possible naming issue) a <script> tag to the DOM - in the search-injection/utils module while the other DOM stuff is in this module. Maybe you could move appendCss and maybe also have a appendReactRoot function to their own search-injection/dom module or something. Just a code-layout suggestion; the actual code seems very well done.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prepend was a very recent change, so forgive me on that. It injects CSS into the page, so I guess injectCSS would fit it. And that's a nice idea, I will do it.

ReactDOM.render(
<Results
results={results.slice(0, limit)}
len={results.length}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

results.length will always be <=10 because of pagination. Instead, where you call handleRender, you can use payload.searchResult.totalCount to get the real number (maybe just pass in the entire payload.searchResult and pull out the needed args: const handleRender = ({ docs, totalCount }) => { )

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I was planning to look into this issue, as it never showed me a length greater than 10.
But I don't think the there is a totalCount attribute in the payload received.
image

@poltak
Copy link
Member

poltak commented Mar 20, 2018 via email

@poltak
Copy link
Member

poltak commented Mar 20, 2018 via email

@digi0ps
Copy link
Contributor Author

digi0ps commented Mar 22, 2018

@poltak @oliversauter @ShishKabab
Wrote few important tests. Modifed according to the comments. Cleaned up the code. And wrote a Readme.

Check it out and tell me if there anything else that needs to be done. Also, should I merge the commits?

}

async componentDidMount() {
const checked = await getLocalStorage(SEARCH_INJECTION_KEY, true)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would name this to something more descriptive. Deeper into the code I see a checkbox is checked, but it would be nice if I could see what it represents immediately, like userDidEnableResults (or probably something better.)

@blackforestboi
Copy link
Member

Notes to @poltak re the tracking:

  1. We want to track active user stats (monthly, weekly, daily) when people actively use the results from the google integration. But not when they just search via google (and results are fetched)
  2. We want to know when people disable the integration alltogether.

package.json Outdated
@@ -71,6 +71,7 @@
"reselect": "^3.0.1",
"response-to-data-url": "^0.1.0",
"rxjs": "^5.5.5",
"sinon": "^4.4.6",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty sure sinon should be a dev dep. (yarn remove sinon && yarn add --dev sinon)

import { getLocalStorage, setLocalStorage } from 'src/search-injection/utils'
import { SEARCH_INJECTION_KEY } from 'src/search-injection/constants'

class Container extends React.Component {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling the class just Container means you have to open it up and look inside to see what it does, as the name doesn't give much info. You also might want to add more containers in this dir later for whatever reason. Maybe SearchInjectionContainer would be better as it simply wraps around that view.

}

const search = query => {
// query: (string) query to be search
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For code doc, I would advise getting familiar with jsdoc which is what we're using in the rest of the project. Not super urgent - comments are better than nothing! - but just to keep in mind for future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh okay, I will take a look at jsdoc.

@poltak
Copy link
Member

poltak commented Mar 23, 2018

@digi0ps there might have been a slight regression in one of the recent changes: content script throws an error now when it mounts the main React component as the props aren't being passed down anymore for some reason.

Also I would remove any console.logs from the content script code; it will log directly to the page's console rather than the extension's console.

@digi0ps
Copy link
Contributor Author

digi0ps commented Mar 27, 2018

@oliversauter @poltak Should I squash any of the commits?

@digi0ps digi0ps force-pushed the search-injection branch 2 times, most recently from d225e9d to 52c41cc Compare March 27, 2018 18:32
digi0ps and others added 22 commits March 28, 2018 13:50
Results are attached only after the DOM is loaded.

elements weren't loaded.
Also:
- Changed the way values are stored in localStorage.
- Remove the need for background.js in search_injection
Also,
- Removed fadeOut of RemovedTest.
- Render React only when document has completed loading.
- Change the logo used.
Also, link it in memex results.
Also, cleans up content_script.js.
And, clean up background.js.
- trakcing when user disables
- user flagged as active when they click a result or click show more to go to overview
- all done from bg script via remote calls
- the settings gear icon had a weird grey background in FF that was sticking out
Also, ran yarn format to clean up code.
@poltak
Copy link
Member

poltak commented Mar 29, 2018

@oliversauter happy to merge this? (let me know and I can resolve the conflicts)

@ShishKabab ShishKabab merged commit 2068880 into WorldBrain:master Mar 29, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants