diff --git a/.gitignore b/.gitignore index 18b43c9cd2..be71d20d6b 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,6 @@ # Ignore master key for decrypting credentials and more. /config/master.key + +# VScode configurations +/.vscode \ No newline at end of file diff --git a/.ruby-version b/.ruby-version index 9aa34646dc..860487ca19 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.7.0 \ No newline at end of file +2.7.1 diff --git a/Gemfile b/Gemfile index 67b156b339..4f3f33e2a2 100644 --- a/Gemfile +++ b/Gemfile @@ -1,10 +1,10 @@ source 'https://rubygems.org' git_source(:github) { |repo| "https://github.com/#{repo}.git" } -ruby '2.7.0' +ruby '2.7.1' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' -gem 'rails', '~> 5.2.4' +gem 'rails', '~> 6.0.3.5' # Use postgresql as the database for Active Record gem 'pg', '>= 0.18', '< 2.0' # Use Puma as the app server @@ -40,12 +40,20 @@ gem 'bootsnap', '>= 1.1.0', require: false gem 'devise' group :development, :test do + # install rspec rails + gem 'rspec-rails', '~> 5.0', '>= 5.0.1' # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: %i[mri mingw x64_mingw] end group :test do - gem 'rspec' + # Adds support for Capybara system testing and selenium driver + gem 'capybara', '>= 2.15' + # Strategies for cleaning databases to ensure a clean slate for testing. + gem 'database_cleaner-active_record' + gem 'selenium-webdriver' + # Easy installation and use of web drivers to run system tests with browsers + gem 'shoulda-matchers', '~> 3.1', '>= 3.1.1' end group :development do @@ -53,7 +61,7 @@ group :development do gem 'listen', '>= 3.0.5', '< 3.2' gem 'web-console', '>= 3.3.0' # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring - gem 'rubocop' + gem 'rubocop', '~> 0.92.0' gem 'spring' gem 'spring-watcher-listen', '~> 2.0.0' end diff --git a/Gemfile.lock b/Gemfile.lock index b67d4b3306..1c23bb8af2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,55 +1,79 @@ GEM remote: https://rubygems.org/ specs: - actioncable (5.2.4.1) - actionpack (= 5.2.4.1) + actioncable (6.0.3.6) + actionpack (= 6.0.3.6) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailer (5.2.4.1) - actionpack (= 5.2.4.1) - actionview (= 5.2.4.1) - activejob (= 5.2.4.1) + actionmailbox (6.0.3.6) + actionpack (= 6.0.3.6) + activejob (= 6.0.3.6) + activerecord (= 6.0.3.6) + activestorage (= 6.0.3.6) + activesupport (= 6.0.3.6) + mail (>= 2.7.1) + actionmailer (6.0.3.6) + actionpack (= 6.0.3.6) + actionview (= 6.0.3.6) + activejob (= 6.0.3.6) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (5.2.4.1) - actionview (= 5.2.4.1) - activesupport (= 5.2.4.1) + actionpack (6.0.3.6) + actionview (= 6.0.3.6) + activesupport (= 6.0.3.6) rack (~> 2.0, >= 2.0.8) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.2.4.1) - activesupport (= 5.2.4.1) + rails-html-sanitizer (~> 1.0, >= 1.2.0) + actiontext (6.0.3.6) + actionpack (= 6.0.3.6) + activerecord (= 6.0.3.6) + activestorage (= 6.0.3.6) + activesupport (= 6.0.3.6) + nokogiri (>= 1.8.5) + actionview (6.0.3.6) + activesupport (= 6.0.3.6) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.0.3) - activejob (5.2.4.1) - activesupport (= 5.2.4.1) + rails-html-sanitizer (~> 1.1, >= 1.2.0) + activejob (6.0.3.6) + activesupport (= 6.0.3.6) globalid (>= 0.3.6) - activemodel (5.2.4.1) - activesupport (= 5.2.4.1) - activerecord (5.2.4.1) - activemodel (= 5.2.4.1) - activesupport (= 5.2.4.1) - arel (>= 9.0) - activestorage (5.2.4.1) - actionpack (= 5.2.4.1) - activerecord (= 5.2.4.1) - marcel (~> 0.3.1) - activesupport (5.2.4.1) + activemodel (6.0.3.6) + activesupport (= 6.0.3.6) + activerecord (6.0.3.6) + activemodel (= 6.0.3.6) + activesupport (= 6.0.3.6) + activestorage (6.0.3.6) + actionpack (= 6.0.3.6) + activejob (= 6.0.3.6) + activerecord (= 6.0.3.6) + marcel (~> 1.0.0) + activesupport (6.0.3.6) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) - arel (9.0.0) - ast (2.4.0) - bcrypt (3.1.13) + zeitwerk (~> 2.2, >= 2.2.2) + addressable (2.7.0) + public_suffix (>= 2.0.2, < 5.0) + ast (2.4.2) + bcrypt (3.1.16) bindex (0.8.1) - bootsnap (1.4.6) + bootsnap (1.7.3) msgpack (~> 1.0) builder (3.2.4) - byebug (11.1.1) + byebug (11.1.3) + capybara (3.35.3) + addressable + mini_mime (>= 0.1.3) + nokogiri (~> 1.8) + rack (>= 1.6.0) + rack-test (>= 0.6.3) + regexp_parser (>= 1.5, < 3.0) + xpath (~> 3.2) + childprocess (3.0.0) coffee-rails (4.2.2) coffee-script (>= 2.2.0) railties (>= 4.0.0) @@ -57,117 +81,129 @@ GEM coffee-script-source execjs coffee-script-source (1.12.2) - concurrent-ruby (1.1.6) + concurrent-ruby (1.1.8) crass (1.0.6) - devise (4.7.1) + database_cleaner-active_record (2.0.0) + activerecord (>= 5.a) + database_cleaner-core (~> 2.0.0) + database_cleaner-core (2.0.1) + devise (4.7.3) bcrypt (~> 3.0) orm_adapter (~> 0.1) railties (>= 4.1.0) responders warden (~> 1.2.3) - diff-lcs (1.3) - erubi (1.9.0) + diff-lcs (1.4.4) + erubi (1.10.0) execjs (2.7.0) - ffi (1.12.2) - ffi (1.12.2-x64-mingw32) + ffi (1.15.0) + ffi (1.15.0-x64-mingw32) globalid (0.4.2) activesupport (>= 4.2.0) - i18n (1.8.2) + i18n (1.8.10) concurrent-ruby (~> 1.0) - jaro_winkler (1.5.4) - jbuilder (2.10.0) + jbuilder (2.11.2) activesupport (>= 5.0.0) listen (3.1.5) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) ruby_dep (~> 1.2) - loofah (2.4.0) + loofah (2.9.0) crass (~> 1.0.2) nokogiri (>= 1.5.9) mail (2.7.1) mini_mime (>= 0.1.1) - marcel (0.3.3) - mimemagic (~> 0.3.2) - method_source (0.9.2) - mimemagic (0.3.4) - mini_mime (1.0.2) + marcel (1.0.1) + method_source (1.0.0) + mini_mime (1.1.0) mini_portile2 (2.5.0) - minitest (5.14.0) - msgpack (1.3.3) - msgpack (1.3.3-x64-mingw32) - nio4r (2.5.2) - nokogiri (1.11.1) + minitest (5.14.4) + msgpack (1.4.2) + nio4r (2.5.7) + nokogiri (1.11.2) mini_portile2 (~> 2.5.0) racc (~> 1.4) - nokogiri (1.11.1-x64-mingw32) + nokogiri (1.11.2-x64-mingw32) racc (~> 1.4) orm_adapter (0.5.0) - parallel (1.19.1) - parser (2.7.0.4) - ast (~> 2.4.0) - pg (1.2.2) - pg (1.2.2-x64-mingw32) + parallel (1.20.1) + parser (3.0.0.0) + ast (~> 2.4.1) + pg (1.2.3) + pg (1.2.3-x64-mingw32) + public_suffix (4.0.6) puma (3.12.6) racc (1.5.2) rack (2.2.3) rack-test (1.1.0) rack (>= 1.0, < 3) - rails (5.2.4.1) - actioncable (= 5.2.4.1) - actionmailer (= 5.2.4.1) - actionpack (= 5.2.4.1) - actionview (= 5.2.4.1) - activejob (= 5.2.4.1) - activemodel (= 5.2.4.1) - activerecord (= 5.2.4.1) - activestorage (= 5.2.4.1) - activesupport (= 5.2.4.1) + rails (6.0.3.6) + actioncable (= 6.0.3.6) + actionmailbox (= 6.0.3.6) + actionmailer (= 6.0.3.6) + actionpack (= 6.0.3.6) + actiontext (= 6.0.3.6) + actionview (= 6.0.3.6) + activejob (= 6.0.3.6) + activemodel (= 6.0.3.6) + activerecord (= 6.0.3.6) + activestorage (= 6.0.3.6) + activesupport (= 6.0.3.6) bundler (>= 1.3.0) - railties (= 5.2.4.1) + railties (= 6.0.3.6) sprockets-rails (>= 2.0.0) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) rails-html-sanitizer (1.3.0) loofah (~> 2.3) - railties (5.2.4.1) - actionpack (= 5.2.4.1) - activesupport (= 5.2.4.1) + railties (6.0.3.6) + actionpack (= 6.0.3.6) + activesupport (= 6.0.3.6) method_source rake (>= 0.8.7) - thor (>= 0.19.0, < 2.0) + thor (>= 0.20.3, < 2.0) rainbow (3.0.0) - rake (13.0.1) - rb-fsevent (0.10.3) + rake (13.0.3) + rb-fsevent (0.10.4) rb-inotify (0.10.1) ffi (~> 1.0) - responders (3.0.0) + regexp_parser (2.1.1) + responders (3.0.1) actionpack (>= 5.0) railties (>= 5.0) rexml (3.2.4) - rspec (3.9.0) - rspec-core (~> 3.9.0) - rspec-expectations (~> 3.9.0) - rspec-mocks (~> 3.9.0) - rspec-core (3.9.1) - rspec-support (~> 3.9.1) - rspec-expectations (3.9.1) + rspec-core (3.10.1) + rspec-support (~> 3.10.0) + rspec-expectations (3.10.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.9.0) - rspec-mocks (3.9.1) + rspec-support (~> 3.10.0) + rspec-mocks (3.10.2) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.9.0) - rspec-support (3.9.2) - rubocop (0.80.1) - jaro_winkler (~> 1.5.1) + rspec-support (~> 3.10.0) + rspec-rails (5.0.1) + actionpack (>= 5.2) + activesupport (>= 5.2) + railties (>= 5.2) + rspec-core (~> 3.10) + rspec-expectations (~> 3.10) + rspec-mocks (~> 3.10) + rspec-support (~> 3.10) + rspec-support (3.10.2) + rubocop (0.92.0) parallel (~> 1.10) - parser (>= 2.7.0.1) + parser (>= 2.7.1.5) rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.7) rexml + rubocop-ast (>= 0.5.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 1.4.0, < 1.7) - ruby-progressbar (1.10.1) + unicode-display_width (>= 1.4.0, < 2.0) + rubocop-ast (1.4.1) + parser (>= 2.7.1.5) + ruby-progressbar (1.11.0) ruby_dep (1.5.0) + rubyzip (2.3.0) sass (3.7.4) sass-listen (~> 4.0.0) sass-listen (4.0.0) @@ -179,40 +215,48 @@ GEM sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) - spring (2.1.0) + selenium-webdriver (3.142.7) + childprocess (>= 0.5, < 4.0) + rubyzip (>= 1.2.2) + shoulda-matchers (3.1.3) + activesupport (>= 4.0.0) + spring (2.1.1) spring-watcher-listen (2.0.1) listen (>= 2.7, < 4.0) spring (>= 1.2, < 3.0) sprockets (3.7.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) - sprockets-rails (3.2.1) + sprockets-rails (3.2.2) actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) - thor (1.0.1) + thor (1.1.0) thread_safe (0.3.6) tilt (2.0.10) turbolinks (5.2.1) turbolinks-source (~> 5.2) turbolinks-source (5.2.0) - tzinfo (1.2.6) + tzinfo (1.2.9) thread_safe (~> 0.1) - tzinfo-data (1.2020.1) + tzinfo-data (1.2021.1) tzinfo (>= 1.0.0) uglifier (4.2.0) execjs (>= 0.3.0, < 3) - unicode-display_width (1.6.1) - warden (1.2.8) - rack (>= 2.0.6) - web-console (3.7.0) - actionview (>= 5.0) - activemodel (>= 5.0) + unicode-display_width (1.7.0) + warden (1.2.9) + rack (>= 2.0.9) + web-console (4.1.0) + actionview (>= 6.0.0) + activemodel (>= 6.0.0) bindex (>= 0.4.0) - railties (>= 5.0) - websocket-driver (0.7.1) + railties (>= 6.0.0) + websocket-driver (0.7.3) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) + xpath (3.2.0) + nokogiri (~> 1.8) + zeitwerk (2.4.2) PLATFORMS ruby @@ -221,16 +265,20 @@ PLATFORMS DEPENDENCIES bootsnap (>= 1.1.0) byebug + capybara (>= 2.15) coffee-rails (~> 4.2) + database_cleaner-active_record devise jbuilder (~> 2.5) listen (>= 3.0.5, < 3.2) pg (>= 0.18, < 2.0) puma (~> 3.12) - rails (~> 5.2.4) - rspec - rubocop + rails (~> 6.0.3.5) + rspec-rails (~> 5.0, >= 5.0.1) + rubocop (~> 0.92.0) sass-rails (~> 5.0) + selenium-webdriver + shoulda-matchers (~> 3.1, >= 3.1.1) spring spring-watcher-listen (~> 2.0.0) turbolinks (~> 5) @@ -239,7 +287,7 @@ DEPENDENCIES web-console (>= 3.3.0) RUBY VERSION - ruby 2.7.0p0 + ruby 2.7.1p83 BUNDLED WITH - 2.1.2 + 2.1.4 diff --git a/README.md b/README.md index e2ff61f120..b6e452e6c3 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ # Scaffold for social media app with Ruby on Rails -> This repo includes intial code for social media app with basic styling. Its purpose is to be a starting point for Microverse students. +> This repo includes code for basic social media app built with rails. The app allows users to create account, see other users, invite them to friendship, and accept or reject thier friendship requests. A user also can create posts, publish them on their timeline, and see his friends' posts -## Built With +## Live demo -- Ruby v2.7.0 -- Ruby on Rails v5.2.4 +View the demo [here](https://fierce-ocean-92729.herokuapp.com/) -## Live Demo +## Built With -TBA +- Ruby v2.7.0 +- Ruby on Rails v6.0.3 ## Getting Started @@ -24,7 +24,7 @@ Postgres: >=9.5 ### Setup -Instal gems with: +Install gems with: ``` bundle install @@ -37,16 +37,6 @@ Setup database with: rails db:migrate ``` -### Github Actions - -To make sure the linters' checks using Github Actions work properly, you should follow the next steps: - -1. On your recently forked repo, enable the GitHub Actions in the Actions tab. -2. Create the `feature/branch` and push. -3. Start working on your milestone as usual. -4. Open a PR from the `feature/branch` when your work is done. - - ### Usage Start server with: @@ -65,13 +55,13 @@ Open `http://localhost:3000/` in your browser. > Tests will be added by Microverse students. There are no tests for initial features in order to make sure that students write all tests from scratch. -### Deployment - -TBA - ## Authors -TBA +👤 **Hubert MUGABO** + +- Github: [@mugberto](https://github.com/mugberto) +- Twitter: [@mugberto](https://twitter.com/mugberto) +- Linkedin: [mugberto](https://www.linkedin.com/in/hubert-mugabo-23144b6a/) ## 🤝 Contributing @@ -85,9 +75,9 @@ Give a ⭐️ if you like this project! ## Acknowledgments -TBA +- Project inspired by Microverse Program ## 📝 License -TBA +This project is MIT Lisenced diff --git a/app/assets/stylesheets/custom.scss b/app/assets/stylesheets/custom.scss index aa50459d37..6fa8a3b204 100644 --- a/app/assets/stylesheets/custom.scss +++ b/app/assets/stylesheets/custom.scss @@ -177,7 +177,26 @@ form { text-transform: uppercase; font-weight: 900; } +} + +.button_to { + display: inline-block; + width: fit-content; + + input[type="submit"] { + padding: 8px; + width: fit-content; + border: none; + border-radius: 4px; + background-color: $primary-color-dark; + color: $primary-color-light; + text-transform: uppercase; + font-weight: 900; + } +} +form, +.button_to { input[type="submit"]:hover { background-color: $primary-color; color: #fff; @@ -275,11 +294,13 @@ form { } } -.users-section { +.users-section, +.invitations-section { width: 100%; margin: 10px; - .users-list li { + .users-list li, + .invitations-list li { margin-bottom: 16px; text-transform: capitalize; font-size: 1.2em; @@ -330,6 +351,7 @@ form { .new_post, .timeline, .users-section, + .invitations-section, .user-section { width: 70%; } @@ -343,7 +365,34 @@ form { .new_post, .timeline, .users-section, + .invitations-section, .user-section { width: 50%; } } + +.pending_badge { + padding: 8px; + width: fit-content; + border: none; + border-radius: 4px; + background-color: $divider-color; + color: $primary-color-text; + font-size: 0.8rem; + text-transform: uppercase; +} + +.un-friend { + padding: 8px; + width: fit-content; + border: none; + border-radius: 4px; + background-color: rgb(255, 103, 103); + color: white; + font-size: 0.8rem; + text-transform: uppercase; +} + +.un-friend:hover { + background-color: rgb(116, 41, 41); +} diff --git a/app/assets/stylesheets/friendship_invitations.scss b/app/assets/stylesheets/friendship_invitations.scss new file mode 100644 index 0000000000..3fba4cb317 --- /dev/null +++ b/app/assets/stylesheets/friendship_invitations.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the friendship_invitations controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/API/api_controller.rb b/app/controllers/API/api_controller.rb new file mode 100644 index 0000000000..be0ae11d0e --- /dev/null +++ b/app/controllers/API/api_controller.rb @@ -0,0 +1,2 @@ +class API::ApiController < ActionController::API +end diff --git a/app/controllers/friendships_controller.rb b/app/controllers/friendships_controller.rb new file mode 100644 index 0000000000..f2967ccdac --- /dev/null +++ b/app/controllers/friendships_controller.rb @@ -0,0 +1,42 @@ +class FriendshipsController < ApplicationController + def index + @invitations = current_user.friend_requests + end + + def create + invitation = Friendship.new(invitation_params) + if invitation.save + redirect_to users_path, notice: 'Invitation sent' + else + flash.now.alert = 'Error' + render users_path + end + end + + def update + invitation = current_user.inverted_friendships.find_by(user_id: invitation_params[:user_id]) + if invitation.confirm_friend + redirect_to invitations_path, notice: "#{invitation.user.name} is your friend now!" + else + flash.now.alert = 'Error' + render users_path + end + end + + def destroy + if params[:status] == 'invitation' + current_user.inverted_friendships.find_by(user_id: params[:id]).destroy + redirect_to users_path, notice: 'Friendship canceled' + elsif params[:status] == 'friendship' + current_user.friendships.find_by(friend_id: params[:id]).destroy + current_user.inverted_friendships.find_by(user_id: params[:id]).destroy + redirect_to users_path, notice: 'Friendship canceled' + end + end + + private + + def invitation_params + params.require(:invitation).permit(:user_id, :friend_id) + end +end diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index 0062aebe55..f1ea61a994 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -20,7 +20,7 @@ def create private def timeline_posts - @timeline_posts ||= Post.all.ordered_by_most_recent.includes(:user) + @timeline_posts = current_user.friends_and_own_posts end def post_params diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index ea52901dfe..066951557a 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,4 +1,20 @@ module ApplicationHelper + def sign_in_or_sign_out + if current_user + link_to 'Sign out', destroy_user_session_path, method: :delete + else + link_to 'Sign in', user_session_path + end + end + + def notifications + if notice.present? + content_tag :div, content_tag(:div, notice), class: 'notice' + elsif alert.present? + content_tag :div, content_tag(:div, alert), class: 'alert' + end + end + def menu_link_to(link_text, link_path) class_name = current_page?(link_path) ? 'menu-item active' : 'menu-item' diff --git a/app/helpers/friendship_helper.rb b/app/helpers/friendship_helper.rb new file mode 100644 index 0000000000..3af07fd81a --- /dev/null +++ b/app/helpers/friendship_helper.rb @@ -0,0 +1,11 @@ +module FriendshipHelper + def confirm_friendship_btn(inviter) + button_to 'Confirm friendship',\ + confirm_invitation_path,\ + params: { invitation: { user_id: inviter.id, friend_id: current_user.id } } + end + + def reject_friendship_btn(inviter) + link_to 'Reject friendship', reject_invitation_path(inviter.id, 'friendship'), class: 'un-friend', method: :delete + end +end diff --git a/app/helpers/user_helper.rb b/app/helpers/user_helper.rb new file mode 100644 index 0000000000..ac405729b7 --- /dev/null +++ b/app/helpers/user_helper.rb @@ -0,0 +1,31 @@ +module UserHelper + def invite_to_friendship_btn(user) + return if user == current_user or current_user.friend? user + + if current_user.pending_friends.include? user + content_tag :span, 'Pending request', class: 'pending_badge' + elsif current_user.invited_by? user + render partial: 'shared/confirm_reject_btn', locals: { user: user } + else + button_to 'Invite to friendship',\ + send_invitation_path,\ + params: { invitation: { user_id: current_user.id, friend_id: user.id } } + end + end + + def cancel_friendship_btn(user) + return unless current_user.friend? user + + link_to 'Undo friendship', reject_invitation_path(user.id, 'friendship'), method: :delete, class: 'un-friend' + end + + def reject_friendship_btn(user) + link_to('Reject Friendship', reject_invitation_path(user.id, 'invitation'), method: :delete, class: 'un-friend') + end + + def confirm_friendship_btn(user) + button_to('Confirm friendship',\ + confirm_invitation_path,\ + params: { invitation: { user_id: user.id, friend_id: current_user.id } }) + end +end diff --git a/app/models/friendship.rb b/app/models/friendship.rb new file mode 100644 index 0000000000..d8d58e2ab6 --- /dev/null +++ b/app/models/friendship.rb @@ -0,0 +1,20 @@ +class Friendship < ApplicationRecord + belongs_to :user + belongs_to :friend, class_name: 'User', foreign_key: 'friend_id' + + def confirm_friend + update_attributes(confirmed: true) + Friendship.create!(friend_id: user_id, + user_id: friend_id, + confirmed: true) + end + + def destroy_friendship + if confirmed == true + Friendship.find_by(friend_id: user_id).destroy + destroy + elsif confirmed == false + destroy + end + end +end diff --git a/app/models/post.rb b/app/models/post.rb index 39ebdc54cb..74c2bf124d 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -1,6 +1,6 @@ class Post < ApplicationRecord - validates :content, presence: true, length: { maximum: 1000, - too_long: '1000 characters in post is the maximum allowed.' } + validates :content, presence: true, + length: { maximum: 1000, too_long: '1000 characters in post is the maximum allowed.' } belongs_to :user diff --git a/app/models/user.rb b/app/models/user.rb index e97f1363c0..798678303c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -9,4 +9,24 @@ class User < ApplicationRecord has_many :posts has_many :comments, dependent: :destroy has_many :likes, dependent: :destroy + has_many :inverted_friendships, class_name: 'Friendship', foreign_key: 'friend_id', dependent: :destroy + has_many :friendships, class_name: 'Friendship', foreign_key: 'user_id', dependent: :destroy + has_many :confirmed_friendships, -> { where confirmed: true }, class_name: 'Friendship' + has_many :friends, through: :confirmed_friendships + has_many :pending_friendships, -> { where confirmed: false }, class_name: 'Friendship', foreign_key: 'user_id' + has_many :pending_friends, through: :pending_friendships, source: :friend + has_many :unconfirmed_friendships, -> { where confirmed: false }, class_name: 'Friendship', foreign_key: 'friend_id' + has_many :friend_requests, through: :unconfirmed_friendships, source: :user + + def friend?(user) + friends.include? user + end + + def invited_by?(user) + friend_requests.include? user + end + + def friends_and_own_posts + Post.where(user: (friends.to_a << self)).ordered_by_most_recent + end end diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb index cf595d517b..e12d287c88 100644 --- a/app/views/devise/registrations/new.html.erb +++ b/app/views/devise/registrations/new.html.erb @@ -27,7 +27,7 @@
- <%= f.submit "Sign up" %> + <%= f.submit "SIGN UP" %>
<% end %> diff --git a/app/views/friendships/_invitation.html.erb b/app/views/friendships/_invitation.html.erb new file mode 100644 index 0000000000..6015c3f803 --- /dev/null +++ b/app/views/friendships/_invitation.html.erb @@ -0,0 +1,8 @@ +
  • + Name: <%= invitation.name %> + + <%= link_to 'See Profile', user_path(invitation), class: 'profile-link' %> + + <%= confirm_friendship_btn invitation %> + <%= reject_friendship_btn invitation %> +
  • \ No newline at end of file diff --git a/app/views/friendships/create.html.erb b/app/views/friendships/create.html.erb new file mode 100644 index 0000000000..9002a6ccaf --- /dev/null +++ b/app/views/friendships/create.html.erb @@ -0,0 +1,2 @@ +

    FriendshipInvitations#create

    +

    Find me in app/views/friendship_invitations/create.html.erb

    diff --git a/app/views/friendships/destroy.html.erb b/app/views/friendships/destroy.html.erb new file mode 100644 index 0000000000..0c8e4aa1f3 --- /dev/null +++ b/app/views/friendships/destroy.html.erb @@ -0,0 +1,2 @@ +

    FriendshipInvitations#destroy

    +

    Find me in app/views/friendship_invitations/destroy.html.erb

    diff --git a/app/views/friendships/index.html.erb b/app/views/friendships/index.html.erb new file mode 100644 index 0000000000..ae894f776e --- /dev/null +++ b/app/views/friendships/index.html.erb @@ -0,0 +1,5 @@ +
    + +
    diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index e5fdd63777..9208df9452 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -17,23 +17,13 @@ + - <% if current_user %> - <%= link_to 'Sign out', destroy_user_session_path, method: :delete %> - <% else %> - <%= link_to 'Sign in', user_session_path %> - <% end %> + <%= sign_in_or_sign_out %> - <% if notice.present? %> -
    -

    <%= notice %>

    -
    - <% end %> - <% if alert.present? %> -
    -

    <%= alert %>

    -
    - <% end %> + <%= notifications %>
    <%= yield %>
    diff --git a/app/views/shared/_confirm_reject_btn.html.erb b/app/views/shared/_confirm_reject_btn.html.erb new file mode 100644 index 0000000000..9a685f4151 --- /dev/null +++ b/app/views/shared/_confirm_reject_btn.html.erb @@ -0,0 +1,5 @@ +<%= button_to('Confirm friendship',\ + confirm_invitation_path,\ + params: { invitation: { user_id: user.id, friend_id: current_user.id } }) %> + +<%= link_to('Reject Friendship', reject_invitation_path(user, 'invitation'), method: :delete, class: 'un-friend') %> \ No newline at end of file diff --git a/app/views/users/_user.html.erb b/app/views/users/_user.html.erb index e03c4beb3f..d9f7d9be10 100644 --- a/app/views/users/_user.html.erb +++ b/app/views/users/_user.html.erb @@ -3,4 +3,6 @@ <%= link_to 'See Profile', user_path(user), class: 'profile-link' %> + <%= invite_to_friendship_btn user %> + <%= cancel_friendship_btn user %> \ No newline at end of file diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index e9919805de..9656cf4f41 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -1,5 +1,8 @@
    -

    <%= "Name: #{@user.name}" %>

    +

    + <%= "Name: #{@user.name}" %> + <%= invite_to_friendship_btn @user %> +

    Recent posts: