Remove UserHistory Feature and PageViews from Algolia (#6127) [deploy]
This commit is contained in:
parent
a7dc893f50
commit
98f25e2b9d
15 changed files with 0 additions and 385 deletions
|
|
@ -1,17 +0,0 @@
|
|||
class HistoryController < ApplicationController
|
||||
before_action :authenticate_user!
|
||||
before_action :generate_algolia_search_key
|
||||
|
||||
def index
|
||||
authorize current_user, :pro_user?
|
||||
@history_index = true # used exclusively by the ERb templates
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def generate_algolia_search_key
|
||||
params = { filters: "viewable_by:#{current_user.id}" }
|
||||
key = ApplicationConfig["ALGOLIASEARCH_SEARCH_ONLY_KEY"]
|
||||
@secured_algolia_key = Algolia.generate_secured_api_key(key, params)
|
||||
end
|
||||
end
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<History /> renders properly 1`] = `
|
||||
<div
|
||||
class="home item-list"
|
||||
>
|
||||
<div
|
||||
class="side-bar"
|
||||
>
|
||||
<div
|
||||
class="widget filters"
|
||||
>
|
||||
<input
|
||||
placeHolder="search your history"
|
||||
/>
|
||||
<div
|
||||
class="tags"
|
||||
>
|
||||
<a
|
||||
class="tag "
|
||||
data-no-instant={true}
|
||||
href="/t/discuss"
|
||||
onClick={[Function]}
|
||||
>
|
||||
#discuss
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="items-container"
|
||||
>
|
||||
<div
|
||||
class="results "
|
||||
>
|
||||
<div
|
||||
class="results-header"
|
||||
>
|
||||
History (empty)
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="items-empty"
|
||||
>
|
||||
<h1>
|
||||
Your History is Lonely
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
import { h } from 'preact';
|
||||
import render from 'preact-render-to-json';
|
||||
import { History } from '../history';
|
||||
|
||||
describe('<History />', () => {
|
||||
it('renders properly', () => {
|
||||
const tree = render(<History availableTags={['discuss']} />);
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
import { h, Component } from 'preact';
|
||||
import { PropTypes } from 'preact-compat';
|
||||
import debounce from 'lodash.debounce';
|
||||
|
||||
import {
|
||||
defaultState,
|
||||
loadNextPage,
|
||||
onSearchBoxType,
|
||||
performInitialSearch,
|
||||
search,
|
||||
toggleTag,
|
||||
} from '../searchableItemList/searchableItemList';
|
||||
import { ItemListLoadMoreButton } from '../src/components/ItemList/ItemListLoadMoreButton';
|
||||
import { ItemListTags } from '../src/components/ItemList/ItemListTags';
|
||||
import { ItemListItem } from '../src/components/ItemList/ItemListItem';
|
||||
|
||||
export class History extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const { availableTags } = this.props;
|
||||
this.state = defaultState({ availableTags });
|
||||
|
||||
// bind and initialize all shared functions
|
||||
this.onSearchBoxType = debounce(onSearchBoxType.bind(this), 300, {
|
||||
leading: true,
|
||||
});
|
||||
this.loadNextPage = loadNextPage.bind(this);
|
||||
this.performInitialSearch = performInitialSearch.bind(this);
|
||||
this.search = search.bind(this);
|
||||
this.toggleTag = toggleTag.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { hitsPerPage } = this.state;
|
||||
|
||||
this.performInitialSearch({
|
||||
containerId: 'history',
|
||||
indexName: 'UserHistory',
|
||||
searchOptions: {
|
||||
hitsPerPage,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
renderEmptyItems() {
|
||||
const { selectedTags, query } = this.state;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="items-empty">
|
||||
<h1>
|
||||
{selectedTags.length === 0 && query.length === 0
|
||||
? 'Your History is Lonely'
|
||||
: 'Nothing with this filter 🤔'}
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
items,
|
||||
itemsLoaded,
|
||||
totalCount,
|
||||
availableTags,
|
||||
selectedTags,
|
||||
showLoadMoreButton,
|
||||
} = this.state;
|
||||
|
||||
const itemsToRender = items.map(item => <ItemListItem item={item} />);
|
||||
|
||||
return (
|
||||
<div className="home item-list">
|
||||
<div className="side-bar">
|
||||
<div className="widget filters">
|
||||
<input
|
||||
onKeyUp={this.onSearchBoxTyping}
|
||||
placeHolder="search your history"
|
||||
/>
|
||||
|
||||
<ItemListTags
|
||||
availableTags={availableTags}
|
||||
selectedTags={selectedTags}
|
||||
onClick={this.toggleTag}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="items-container">
|
||||
<div className={`results ${itemsLoaded ? 'results--loaded' : ''}`}>
|
||||
<div className="results-header">
|
||||
History
|
||||
{` (${totalCount > 0 ? totalCount : 'empty'})`}
|
||||
</div>
|
||||
{items.length > 0 ? itemsToRender : this.renderEmptyItems()}
|
||||
</div>
|
||||
|
||||
<ItemListLoadMoreButton
|
||||
show={showLoadMoreButton}
|
||||
onClick={this.loadNextPage}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
History.propTypes = {
|
||||
availableTags: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
};
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
import { h, render } from 'preact';
|
||||
import { getUserDataAndCsrfToken } from '../chat/util';
|
||||
import { History } from '../history/history';
|
||||
|
||||
function loadComponent() {
|
||||
getUserDataAndCsrfToken().then(({ currentUser }) => {
|
||||
const root = document.getElementById('history');
|
||||
if (root) {
|
||||
render(
|
||||
<History availableTags={currentUser.followed_tag_names} />,
|
||||
root,
|
||||
root.firstElementChild,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.InstantClick.on('change', () => {
|
||||
loadComponent();
|
||||
});
|
||||
|
||||
loadComponent();
|
||||
|
|
@ -1,59 +1,9 @@
|
|||
class PageView < ApplicationRecord
|
||||
include AlgoliaSearch
|
||||
|
||||
belongs_to :user, optional: true
|
||||
belongs_to :article
|
||||
|
||||
before_create :extract_domain_and_path
|
||||
|
||||
algoliasearch index_name: "UserHistory", per_environment: true, if: :belongs_to_pro_user?, enqueue: :trigger_index_sync do
|
||||
attributes :referrer, :user_agent, :article_tags
|
||||
|
||||
attribute(:article_title) { article.title }
|
||||
attribute(:article_path) { article.path }
|
||||
attribute(:article_reading_time) { article.reading_time }
|
||||
attribute(:viewable_by) { user_id }
|
||||
attribute(:visited_at_timestamp) { created_at.to_i }
|
||||
|
||||
attribute :article_user do
|
||||
user = article.user
|
||||
{
|
||||
username: user.username,
|
||||
name: user.name,
|
||||
profile_image_90: user.profile_image_90
|
||||
}
|
||||
end
|
||||
|
||||
attribute :readable_visited_at do
|
||||
if created_at.year == Time.current.year
|
||||
created_at.strftime("%b %e")
|
||||
else
|
||||
created_at.strftime("%b %e '%y")
|
||||
end
|
||||
end
|
||||
|
||||
searchableAttributes(
|
||||
%i[referrer user_agent article_title article_searchable_tags article_searchable_text],
|
||||
)
|
||||
|
||||
tags { article_tags }
|
||||
|
||||
attributesForFaceting ["filterOnly(viewable_by)"]
|
||||
|
||||
attributeForDistinct :article_path
|
||||
distinct true
|
||||
|
||||
customRanking ["desc(visited_at_timestamp)"]
|
||||
end
|
||||
|
||||
def self.trigger_index_sync(record, remove)
|
||||
if remove
|
||||
Search::RemoveFromIndexWorker.perform_async(algolia_index_name, record.id)
|
||||
else
|
||||
Search::IndexWorker.perform_async("PageView", record.id)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def extract_domain_and_path
|
||||
|
|
@ -64,10 +14,6 @@ class PageView < ApplicationRecord
|
|||
self.path = parsed_url.path
|
||||
end
|
||||
|
||||
def belongs_to_pro_user?
|
||||
user&.pro?
|
||||
end
|
||||
|
||||
def article_searchable_tags
|
||||
article.cached_tag_list
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10,8 +10,6 @@ module ProMemberships
|
|||
|
||||
def call
|
||||
if purchase_pro_membership
|
||||
ProMemberships::PopulateHistoryWorker.perform_async(user.id)
|
||||
|
||||
channel = ChatChannel.find_by(slug: "pro-members")
|
||||
channel&.add_users(user)
|
||||
|
||||
|
|
|
|||
|
|
@ -26,9 +26,6 @@
|
|||
<a class="sidebar-nav-link trusted-visible-block" href="<%= mod_path %>">
|
||||
<img class="nav-emoji" src="<%= asset_path("emoji/emoji-one-sloth.png") %>" alt="moderation icon" /> Moderation
|
||||
</a>
|
||||
<a class="sidebar-nav-link pro-visible-block" href="<%= history_path %>">
|
||||
<img class="nav-emoji" src="<%= asset_path("emoji/emoji-one-filmstrip.png") %>" alt="history icon" /> History
|
||||
</a>
|
||||
<a class="sidebar-nav-link" href="<%= classified_listings_path %>">
|
||||
<img class="nav-emoji" src="<%= asset_path("emoji/emoji-one-clipboard.png") %>" alt="listings icon" /> Listings
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
<% title "History" %>
|
||||
|
||||
<%= content_for :page_meta do %>
|
||||
<% page_url = "#{ApplicationConfig['APP_PROTOCOL']}#{ApplicationConfig['APP_DOMAIN']}#{history_path}" %>
|
||||
<link rel="canonical" href="<%= page_url %>" />
|
||||
<meta name="description" content="Page views history for <%= community_qualified_name %>">
|
||||
<meta name="keywords" content="software development,engineering,rails,javascript,ruby">
|
||||
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="og:url" content="<%= page_url %>" />
|
||||
<meta property="og:title" content="History - <%= community_qualified_name %>" />
|
||||
<meta property="og:description" content="My Posts History on <%= community_qualified_name %>" />
|
||||
<meta property="og:site_name" content="<%= community_qualified_name %>" />
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:site" content="@<%= SiteConfig.social_networks_handle %>">
|
||||
<meta name="twitter:title" content="History - <%= community_qualified_name %>">
|
||||
<meta name="twitter:description" content="Page views history for <%= community_qualified_name %>">
|
||||
<% end %>
|
||||
|
||||
<div class="home">
|
||||
<div class="item-list-container" id="history" data-algolia-key="<%= @secured_algolia_key %>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= javascript_pack_tag "history", defer: true %>
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
module ProMemberships
|
||||
class PopulateHistoryWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: :medium_priority, retry: 10
|
||||
|
||||
def perform(user_id)
|
||||
user = User.find_by(id: user_id)
|
||||
return unless user&.pro?
|
||||
|
||||
user.page_views.reindex!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -361,7 +361,6 @@ Rails.application.routes.draw do
|
|||
get "/podcasts", to: redirect("pod")
|
||||
get "/readinglist" => "reading_list_items#index"
|
||||
get "/readinglist/:view" => "reading_list_items#index", :constraints => { view: /archive/ }
|
||||
get "/history", to: "history#index", as: :history
|
||||
|
||||
get "/feed" => "articles#feed", :as => "feed", :defaults => { format: "rss" }
|
||||
get "/feed/tag/:tag" => "articles#feed",
|
||||
|
|
|
|||
|
|
@ -19,18 +19,4 @@ RSpec.describe PageView, type: :model do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "indexing" do
|
||||
it "indexes updated records" do
|
||||
sidekiq_assert_enqueued_with(job: Search::IndexWorker, args: ["PageView", page_view.id]) do
|
||||
page_view.update(path: "/")
|
||||
end
|
||||
end
|
||||
|
||||
it "removes deleted records" do
|
||||
sidekiq_assert_enqueued_with(job: Search::RemoveFromIndexWorker, args: [described_class.algolia_index_name, page_view.id]) do
|
||||
page_view.destroy
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,28 +0,0 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe "History", type: :request do
|
||||
let(:user) { create(:user) }
|
||||
let(:pro_user) { create(:user, :pro) }
|
||||
let(:pro_membership_user) { create(:user, :with_pro_membership) }
|
||||
|
||||
describe "GET /history" do
|
||||
it "does not allow access to a regular user" do
|
||||
sign_in user
|
||||
expect { get history_path }.to raise_error(Pundit::NotAuthorizedError)
|
||||
end
|
||||
|
||||
it "allows access to a pro user" do
|
||||
sign_in pro_user
|
||||
get history_path
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.body).to include("History")
|
||||
end
|
||||
|
||||
it "allows access to a user with a pro membership" do
|
||||
sign_in pro_membership_user
|
||||
get history_path
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.body).to include("History")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -68,16 +68,6 @@ RSpec.describe "Pro Memberships", type: :request do
|
|||
end.to change(user.credits.spent, :count).by(ProMembership::MONTHLY_COST)
|
||||
end
|
||||
|
||||
it "enqueues a job to populate the history" do
|
||||
sidekiq_assert_enqueued_with(
|
||||
job: ProMemberships::PopulateHistoryWorker,
|
||||
args: [user.id],
|
||||
queue: "medium_priority",
|
||||
) do
|
||||
post pro_membership_path
|
||||
end
|
||||
end
|
||||
|
||||
it "enqueues a job to bust the user's cache" do
|
||||
ActiveJob::Base.queue_adapter.enqueued_jobs.clear # make sure it hasn't been previously queued
|
||||
sidekiq_assert_enqueued_with(job: Users::BustCacheWorker, args: [user.id]) do
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe ProMemberships::PopulateHistoryWorker, type: :worker do
|
||||
include_examples "#enqueues_on_correct_queue", "medium_priority", 1
|
||||
|
||||
describe "#perform" do
|
||||
let(:user) { create(:user, :pro) }
|
||||
|
||||
before do
|
||||
allow(User).to receive(:find_by).and_return(user)
|
||||
allow(user.page_views).to receive(:reindex!)
|
||||
end
|
||||
|
||||
it "indexes user page views" do
|
||||
described_class.new.perform(user.id)
|
||||
expect(user.page_views).to have_received(:reindex!).once
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Reference in a new issue