diff --git a/Gemfile b/Gemfile
index fd2e2b45..01654611 100644
--- a/Gemfile
+++ b/Gemfile
@@ -21,3 +21,5 @@ gem 'turbolinks'
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
gem 'uglifier'
gem 'web-console', group: :development
+
+gem "httparty", "~> 0.22.0"
diff --git a/Gemfile.lock b/Gemfile.lock
index 7d7a3577..695ba413 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -70,6 +70,7 @@ GEM
public_suffix (>= 2.0.2, < 7.0)
base64 (0.2.0)
bcrypt (3.1.20)
+ bigdecimal (3.1.8)
bindex (0.8.1)
builder (3.3.0)
byebug (11.1.3)
@@ -92,6 +93,7 @@ GEM
coffee-script-source (1.12.2)
concurrent-ruby (1.3.4)
crass (1.0.6)
+ csv (3.3.0)
date (3.3.4)
devise (4.9.4)
bcrypt (~> 3.0)
@@ -110,6 +112,12 @@ GEM
ffi (1.17.0)
globalid (1.2.1)
activesupport (>= 6.1)
+ httparty (0.22.0)
+ csv
+ mini_mime (>= 1.0.0)
+ multi_xml (>= 0.5.2)
+ httpparty (0.2.0)
+ httparty (> 0)
i18n (1.14.5)
concurrent-ruby (~> 1.0)
jbuilder (2.12.0)
@@ -133,6 +141,8 @@ GEM
mini_mime (1.1.5)
mini_portile2 (2.8.7)
minitest (5.25.1)
+ multi_xml (0.7.1)
+ bigdecimal (~> 3.1)
net-imap (0.4.14)
date
net-protocol
@@ -275,6 +285,8 @@ DEPENDENCIES
coffee-rails
devise
factory_bot_rails
+ httparty (~> 0.22.0)
+ httpparty (~> 0.2.0)
jbuilder
listen
pg
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 1c07694e..4e5617f5 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -1,3 +1,4 @@
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
+ before_action :authenticate_user!
end
diff --git a/app/controllers/pages_controller.rb b/app/controllers/pages_controller.rb
index ce3bf586..96626a2f 100644
--- a/app/controllers/pages_controller.rb
+++ b/app/controllers/pages_controller.rb
@@ -1,2 +1,12 @@
class PagesController < ApplicationController
+
+ def home
+ stories_flagged = Flag.stories_flagged()
+ @stories = helpers.fetch_story_details(stories_flagged.keys)
+ @stories.each do |story|
+ story_id = story['id']
+ story['flags'] = stories_flagged[story_id].map(&:user).map(&:email).join(', ') if stories_flagged[story_id]
+ end
+ end
+
end
diff --git a/app/controllers/stories_controller.rb b/app/controllers/stories_controller.rb
new file mode 100644
index 00000000..a5590462
--- /dev/null
+++ b/app/controllers/stories_controller.rb
@@ -0,0 +1,29 @@
+class StoriesController < ApplicationController
+
+ def index
+ top_story_ids = helpers.fetch_top_stories.first(10)
+ @stories = helpers.fetch_story_details(top_story_ids)
+ @flagged_story_ids = current_user.flags.pluck(:story_id)
+
+ @stories.each do |story|
+ story_id = story['id']
+ story['flagged'] = @flagged_story_ids.member?(story_id)
+ end
+ end
+
+ def show
+ story_id = params[:id]
+
+ flag = current_user.flags.find_by(story_id: story_id)
+ if flag
+ notice = 'Story was successfully unflagged.'
+ flag.destroy!
+ else
+ notice = 'Story was successfully flagged.'
+ Flag.create!(story_id: story_id, user_id: current_user.id)
+ end
+
+ redirect_to(stories_path, notice: notice)
+ end
+
+end
diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb
new file mode 100644
index 00000000..d2daa38c
--- /dev/null
+++ b/app/controllers/users/sessions_controller.rb
@@ -0,0 +1,2 @@
+class Users::SessionsController < Devise::SessionsController
+end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index de6be794..080f279f 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -1,2 +1,17 @@
module ApplicationHelper
+ @@hacker_base= 'https://hacker-news.firebaseio.com/v0/'
+
+ def fetch_top_stories
+ response = HTTParty.get("#{@@hacker_base}topstories.json")
+ JSON.parse(response.body)
+ end
+
+ def fetch_story_details(story_ids)
+ details = story_ids.map do |id|
+ response = HTTParty.get("#{@@hacker_base}item/#{id}.json")
+ JSON.parse(response.body)
+ end
+
+ details.compact
+ end
end
diff --git a/app/models/flag.rb b/app/models/flag.rb
new file mode 100644
index 00000000..05d2551c
--- /dev/null
+++ b/app/models/flag.rb
@@ -0,0 +1,12 @@
+class Flag < ApplicationRecord
+ belongs_to :user
+
+ validates :story_id, presence: true
+ validates :user_id, presence: true
+ validates :story_id, uniqueness: { scope: :user_id, message: "has already been flagged by this user" }
+
+ def Flag.stories_flagged()
+ flags = Flag.includes(:user).group_by(&:story_id)
+ return flags
+ end
+end
diff --git a/app/models/user.rb b/app/models/user.rb
index b2091f9a..68036eff 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -3,4 +3,6 @@ class User < ApplicationRecord
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
+
+ has_many :flags
end
diff --git a/app/views/application/_story.html.erb b/app/views/application/_story.html.erb
new file mode 100644
index 00000000..27645666
--- /dev/null
+++ b/app/views/application/_story.html.erb
@@ -0,0 +1,24 @@
+ <% @stories.each do |story| %>
+
+
+
+ <%= link_to story['title'], story['url'], target: '_blank' %>
+
+
+
+
+ Score: <%= story['score'] %> |
+ By: <%= story['by'] %> |
+ <%= pluralize(story['descendants'], 'comment') %> |
+ <%= link_to "Hacker News Discussion", "https://news.ycombinator.com/item?id=#{story['id']}", target: '_blank' %>
+
+ <% if story['flags'].present? %>
+
+ Flagged by: <%= story['flags'] %>
+
+ <% end %>
+
+ <%= link_to story['flagged'] ? 'Unflag!' :'Flag!' , story_path(story['id']) %>
+
+
+<% end %>
diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb
new file mode 100644
index 00000000..a793181a
--- /dev/null
+++ b/app/views/devise/sessions/new.html.erb
@@ -0,0 +1,19 @@
+Log in
+
+<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
+
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
+
+
+
+ <%= f.label :password %>
+ <%= f.password_field :password, autocomplete: "current-password" %>
+
+
+
+ <%= f.submit "Log in" %>
+
+<% end %>
+
+<%= render "devise/shared/links" %>
diff --git a/app/views/devise/shared/_error_messages.html.erb b/app/views/devise/shared/_error_messages.html.erb
new file mode 100644
index 00000000..cabfe307
--- /dev/null
+++ b/app/views/devise/shared/_error_messages.html.erb
@@ -0,0 +1,15 @@
+<% if resource.errors.any? %>
+
+
+ <%= I18n.t("errors.messages.not_saved",
+ count: resource.errors.count,
+ resource: resource.class.model_name.human.downcase)
+ %>
+
+
+ <% resource.errors.full_messages.each do |message| %>
+ - <%= message %>
+ <% end %>
+
+
+<% end %>
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index 331a7ed0..934a7531 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -8,6 +8,9 @@
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
+ <% if user_signed_in? %>
+ <%= button_to 'Sign out', destroy_user_session_path, method: :delete %>
+ <% end %>
<%= notice %>
diff --git a/app/views/pages/home.html.erb b/app/views/pages/home.html.erb
index 8bfd8294..2f273d87 100644
--- a/app/views/pages/home.html.erb
+++ b/app/views/pages/home.html.erb
@@ -1 +1,4 @@
-Welcome to Top News
+User Flagged Stories
+<%= link_to 'Current Top Stories', stories_path %>
+<%= render 'story' %>
+
diff --git a/app/views/stories/index.html.erb b/app/views/stories/index.html.erb
new file mode 100644
index 00000000..71f4470d
--- /dev/null
+++ b/app/views/stories/index.html.erb
@@ -0,0 +1,4 @@
+Top Hacker News Stories
+<%= link_to 'User Flagged Stories', '/' %>
+
+<%= render 'story' %>
\ No newline at end of file
diff --git a/config/routes.rb b/config/routes.rb
index c12ef082..8ea039ed 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,4 +1,6 @@
Rails.application.routes.draw do
+ resources :stories, only: [:index, :show]
+
devise_for :users
root to: 'pages#home'
end
diff --git a/db/migrate/20240830133222_create_flags.rb b/db/migrate/20240830133222_create_flags.rb
new file mode 100644
index 00000000..812e4a3f
--- /dev/null
+++ b/db/migrate/20240830133222_create_flags.rb
@@ -0,0 +1,10 @@
+class CreateFlags < ActiveRecord::Migration[7.0]
+ def change
+ create_table :flags do |t|
+ t.integer :story_id
+ t.integer :user_id
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index acc34f3b..d734f838 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,10 +10,17 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[7.0].define(version: 2018_02_28_212101) do
+ActiveRecord::Schema[7.0].define(version: 2024_08_30_133222) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
+ create_table "flags", force: :cascade do |t|
+ t.integer "story_id"
+ t.integer "user_id"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ end
+
create_table "users", force: :cascade do |t|
t.string "first_name"
t.string "last_name"