diff --git a/src/containers/background/_sites.js b/src/containers/background/_sites.js new file mode 100644 index 00000000..ab1d188f --- /dev/null +++ b/src/containers/background/_sites.js @@ -0,0 +1,20 @@ +import { takeLatest, call } from 'redux-saga/effects' +import { requireAuthorization } from '../auth/_auth' +import { saveToPocket } from '../../common/api' + +// SAGAS +export function* wSaveTweet() { + yield takeLatest('SAVE_TWEET_TO_POCKET', saveTweetRequest) +} + +function* saveTweetRequest(saveObject) { + const { permaLink, elementId } = saveObject.request + const authToken = yield call(requireAuthorization) + + const url = `https://twitter.com${permaLink}` + const data = saveToPocket({ url, elementId }, authToken) + + data.then(results => { + saveObject.sendResponse(results) + }) +} diff --git a/src/containers/background/background.js b/src/containers/background/background.js index 3e1c2f6a..9ce897a3 100644 --- a/src/containers/background/background.js +++ b/src/containers/background/background.js @@ -49,6 +49,20 @@ Interface.addMessageListener((request, sender, sendResponse) => { ? store.dispatch(cancelCloseSavePanel({ tabId: sender.tab.id })) : store.dispatch(closeSavePanel({ tabId: sender.tab.id })) } + + if (request.action === 'twitterCheck') { + const twitterState = store.getState() + sendResponse(twitterState.setup && twitterState.setup.sites_twitter) + } + + if (request.action === 'twitterSave') { + store.dispatch({ + type: 'SAVE_TWEET_TO_POCKET', + request, + sendResponse + }) + return true + } }) Interface.contextMenus().removeAll(createContextMenus) diff --git a/src/containers/options/options.app.js b/src/containers/options/options.app.js index 0de18a6c..d18e89e1 100644 --- a/src/containers/options/options.app.js +++ b/src/containers/options/options.app.js @@ -45,8 +45,6 @@ export default class Options extends Component { } toggleTwitter = () => this.props.toggleSite('sites_twitter') - toggleHackernews = () => this.props.toggleSite('sites_hackernews') - toggleReddit = () => this.props.toggleSite('sites_reddit') setShortcuts = () => openTabWithUrl(SET_SHORTCUTS) @@ -111,22 +109,6 @@ export default class Options extends Component { /> Twitter -
  • - - Hacker News -
  • -
  • - - Reddit -
  • {localize('options_page', 'services_info')} diff --git a/src/containers/sites/twitter/twitter.js b/src/containers/sites/twitter/twitter.js index e69de29b..c1e5ada6 100644 --- a/src/containers/sites/twitter/twitter.js +++ b/src/containers/sites/twitter/twitter.js @@ -0,0 +1,124 @@ +import styles from './twitter.scss' // Import Styles +import { addMessageListener, sendMessage } from '../../../common/interface' + +const mutationConfig = { + childList: true, + attributes: false, + characterData: false, + subtree: true +} + +// Set up Observer +const appObserver = new MutationObserver(appMutationHandler) +function appMutationHandler(mutationList) { + for (var mutation of mutationList) { + if ( + mutation.type === 'childList' && + (mutation.target.id === 'page-container' || + mutation.target.id === 'stream-items-id' || + mutation.target.id === 'permalink-overlay-body') + ) { + handleNewItems() + } + } +} + +// Define Markup +const saveToPocketMarkup = ` + +` +const saveToPocketButton = document.createElement('div') +saveToPocketButton.classList.add( + 'ProfileTweet-action', + 'ProfileTweet-action--stp' +) +saveToPocketButton.innerHTML = saveToPocketMarkup + +// Start and Stop integration +function resolveCheck(integrate) { + if (integrate) return startIntegration() + stopIntegration() +} + +function startIntegration() { + appObserver.observe(document, mutationConfig) + handleNewItems() +} + +function stopIntegration() { + appObserver.disconnect() + const nodeList = document.querySelectorAll('div.ProfileTweet-action--stp') + nodeList.forEach(e => e.parentNode.removeChild(e)) +} + +// Set Injections +function handleNewItems() { + const tweetActionLists = document.querySelectorAll( + '.tweet:not(.PocketAdded)' + ) + if (!tweetActionLists.length) return + + Array.from(tweetActionLists, addPocketFunctionality) +} + +function addPocketFunctionality(element) { + const permaLink = element.getAttribute('data-permalink-path') + const elementId = element.getAttribute('data-item-id') + + const buttonClone = saveToPocketButton.cloneNode(true) + buttonClone.id = `pocketButton-${elementId}` + buttonClone.addEventListener( + 'click', + handleSave.bind(this, elementId, permaLink) + ) + + buttonClone.setAttribute('data-permalink-path', permaLink) + buttonClone.setAttribute('data-item-id', elementId) + + const actionList = element.querySelector('.ProfileTweet-actionList') + if (actionList) { + actionList.appendChild(buttonClone) + element.classList.add('PocketAdded') + } +} + +// Handle saving +function handleSave(elementId, permaLink) { + sendMessage( + null, + { action: 'twitterSave', permaLink, elementId }, + resolveSave + ) +} + +function resolveSave(data) { + const elementId = data.saveObject.elementId + const tweet = document.getElementById(`pocketButton-${elementId}`) + tweet.classList.add(styles.saved) +} + +function handleAction(action, sender, sendResponse) { + if (action.type === 'twitterStop') { + stopIntegration() + } + + if (action.type === 'twitterStart') { + startIntegration() + } +} + +addMessageListener(handleAction) + +// Do we want twitter integration? +sendMessage(null, { action: 'twitterCheck' }, resolveCheck) diff --git a/src/containers/sites/twitter/twitter.scss b/src/containers/sites/twitter/twitter.scss new file mode 100644 index 00000000..c9d1563e --- /dev/null +++ b/src/containers/sites/twitter/twitter.scss @@ -0,0 +1,12 @@ +.pocketIcon { + display: inline-block; + fill: currentColor; + height: 16px; + vertical-align: bottom; + width: 16px; + + &:hover, + .saved & { + fill: #ef4056; + } +} diff --git a/src/manifest.yml b/src/manifest.yml index f20dffe1..9f1ab223 100644 --- a/src/manifest.yml +++ b/src/manifest.yml @@ -1,5 +1,5 @@ name: 'Save to Pocket' -version: 3.0.0.8 +version: 3.0.0.9 options_page: options.html description: __MSG_extDescriptionGoogleChrome__ default_locale: "en" @@ -45,19 +45,7 @@ content_scripts: - '*://twitter.com/*' js: - js/twitter.bundle.js - - - matches: - - 'http://*.ycombinator.org/*' - - 'https://*.ycombinator.org/*' - - 'http://*.ycombinator.com/*' - - 'https://*.ycombinator.com/*' - js: - - js/hackerNews.bundle.js - - - matches: - - '*://*.reddit.com/*' - js: - - js/reddit.bundle.js + icons: 128: images/icon-128.png 48: images/icon-48.png diff --git a/src/store/combineSagas.js b/src/store/combineSagas.js index a2efc0ab..5be8595f 100644 --- a/src/store/combineSagas.js +++ b/src/store/combineSagas.js @@ -6,6 +6,7 @@ import { wSetup } from '../containers/background/_setup' import { wHydrate } from '../containers/background/_setup' import { wToggleRecs } from '../containers/background/_setup' import { wToggleSite } from '../containers/background/_setup' +import { wSaveTweet } from '../containers/background/_sites' import { wAuthCodeRecieved } from '../containers/auth/_auth' import { wLogout } from '../containers/auth/_auth' @@ -35,6 +36,7 @@ export default function* rootSaga() { wHydrate(), wToggleRecs(), wToggleSite(), + wSaveTweet(), wOpenPocket(), wOpenOptions(),