From d9217531faac29330cd65bc51744977161bdfc81 Mon Sep 17 00:00:00 2001 From: scouillard Date: Fri, 6 May 2022 16:08:13 -0400 Subject: [PATCH 01/23] Database --- .../20220505192018_create_shared_accesses.rb | 10 ++++++++ db/schema.rb | 25 ++++++++++++++++--- test/fixtures/shared_accesses.yml | 9 +++++++ test/models/shared_access_test.rb | 7 ++++++ 4 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 db/migrate/20220505192018_create_shared_accesses.rb create mode 100644 test/fixtures/shared_accesses.yml create mode 100644 test/models/shared_access_test.rb diff --git a/db/migrate/20220505192018_create_shared_accesses.rb b/db/migrate/20220505192018_create_shared_accesses.rb new file mode 100644 index 0000000000..ea3f720e59 --- /dev/null +++ b/db/migrate/20220505192018_create_shared_accesses.rb @@ -0,0 +1,10 @@ +class CreateSharedAccesses < ActiveRecord::Migration[7.0] + def change + create_table :shared_accesses do |t| + t.references :user, null: false, foreign_key: true + t.references :room, null: false, foreign_key: true + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 3173309f05..31376e0be6 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2022_04_21_185735) do +ActiveRecord::Schema[7.0].define(version: 2022_05_05_192018) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -42,9 +42,6 @@ t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true end - create_table "data_migrations", primary_key: "version", id: :string, force: :cascade do |t| - end - create_table "formats", force: :cascade do |t| t.bigint "recording_id" t.string "recording_type", null: false @@ -97,6 +94,24 @@ t.index ["user_id"], name: "index_rooms_on_user_id" end + create_table "shared_accesses", force: :cascade do |t| + t.bigint "user_id", null: false + t.bigint "room_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["room_id"], name: "index_shared_accesses_on_room_id" + t.index ["user_id"], name: "index_shared_accesses_on_user_id" + end + + create_table "user_rooms", force: :cascade do |t| + t.bigint "user_id" + t.bigint "room_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["room_id"], name: "index_user_rooms_on_room_id" + t.index ["user_id"], name: "index_user_rooms_on_user_id" + end + create_table "users", force: :cascade do |t| t.string "name", null: false t.string "email", null: false @@ -116,4 +131,6 @@ add_foreign_key "room_meeting_options", "meeting_options" add_foreign_key "room_meeting_options", "rooms" add_foreign_key "rooms", "users" + add_foreign_key "shared_accesses", "rooms" + add_foreign_key "shared_accesses", "users" end diff --git a/test/fixtures/shared_accesses.yml b/test/fixtures/shared_accesses.yml new file mode 100644 index 0000000000..6af5c7d837 --- /dev/null +++ b/test/fixtures/shared_accesses.yml @@ -0,0 +1,9 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + user: one + room: one + +two: + user: two + room: two diff --git a/test/models/shared_access_test.rb b/test/models/shared_access_test.rb new file mode 100644 index 0000000000..53af10f41d --- /dev/null +++ b/test/models/shared_access_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class SharedAccessTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end From fea5c056c65335fb8660ab322f50ac76dbc3772b Mon Sep 17 00:00:00 2001 From: scouillard Date: Fri, 6 May 2022 16:08:57 -0400 Subject: [PATCH 02/23] Backend --- app/controllers/api/v1/rooms_controller.rb | 54 ++++++++++++++++++- app/controllers/api/v1/sessions_controller.rb | 6 +-- app/controllers/api/v1/users_controller.rb | 12 +++++ app/controllers/application_controller.rb | 12 +++-- app/models/room.rb | 11 ++++ app/models/shared_access.rb | 7 +++ app/models/user.rb | 4 ++ config/routes.rb | 5 +- 8 files changed, 100 insertions(+), 11 deletions(-) create mode 100644 app/models/shared_access.rb diff --git a/app/controllers/api/v1/rooms_controller.rb b/app/controllers/api/v1/rooms_controller.rb index 09185b832e..2ddbcbe6d5 100644 --- a/app/controllers/api/v1/rooms_controller.rb +++ b/app/controllers/api/v1/rooms_controller.rb @@ -4,7 +4,7 @@ module Api module V1 class RoomsController < ApplicationController skip_before_action :verify_authenticity_token # TODO: amir - Revisit this. - before_action :find_room, only: %i[show start recordings] + before_action :find_room, only: %i[show start recordings shared_access shared_users shareable_users] # GET /api/v1/rooms.json # Returns: { data: Array[serializable objects(rooms)] , errors: Array[String] } @@ -65,6 +65,58 @@ def recordings render_json(data: @room.recordings, status: :ok, include: :formats) end + # POST /api/v1/rooms/friendly_id/shared_access + def shared_access + shared_users = User.where(id: params[:shared_access_users]) + + shared_users.each do |shared_user| + SharedAccess.where(user_id: shared_user.id, room_id: @room.id).first_or_create + end + + render_json status: :ok + end + + # GET /api/v1/rooms/friendly_id/shared_users.json + def shared_users + shared_users = [] + + User.joins(:shared_rooms).each do |user| + shared_users << user if user.shared_rooms.pluck(:friendly_id).include?(@room.friendly_id) + end + + shared_users.map! do |user| + { + id: user.id, + name: user.name, + email: user.email, + avatar: user_avatar(user) + } + end + + render_json data: shared_users, status: :ok + end + + def shareable_users + shareable_users = [] + + User.all.each do |user| + unless user.rooms.pluck(:friendly_id).include?(@room.friendly_id) || user.shared_rooms.pluck(:friendly_id).include?(@room.friendly_id) + shareable_users << user + end + end + + shareable_users.map! do |user| + { + id: user.id, + name: user.name, + email: user.email, + avatar: user_avatar(user) + } + end + + render_json data: shareable_users, status: :ok + end + private def find_room diff --git a/app/controllers/api/v1/sessions_controller.rb b/app/controllers/api/v1/sessions_controller.rb index 3621f78725..c2fe679469 100644 --- a/app/controllers/api/v1/sessions_controller.rb +++ b/app/controllers/api/v1/sessions_controller.rb @@ -15,7 +15,7 @@ def index name: current_user.name, email: current_user.email, provider: current_user.provider, - avatar: current_user_avatar, + avatar: user_avatar(current_user), signed_in: true } } @@ -52,10 +52,6 @@ def session_params def sign_in(user) session[:user_id] = user.id end - - def current_user_avatar - current_user.avatar.attached? ? url_for(current_user.avatar) : ActionController::Base.helpers.image_path('default-avatar.png') - end end end end diff --git a/app/controllers/api/v1/users_controller.rb b/app/controllers/api/v1/users_controller.rb index a31bb75b38..d0e72eb014 100644 --- a/app/controllers/api/v1/users_controller.rb +++ b/app/controllers/api/v1/users_controller.rb @@ -10,6 +10,18 @@ class UsersController < ApplicationController # Returns: { data: Array[serializable objects] , errors: Array[String] } # Does: Creates and saves a new user record in the database with the provided parameters. + def index + users = User.all.map do |user| + { + id: user.id, + name: user.name, + email: user.email, + avatar: user_avatar(user) + } + end + render_json data: users, status: :ok + end + def create # TODO: amir - ensure accessibility for unauthenticated requests only. user = User.new({ provider: 'greenlight' }.merge(user_params)) # TMP fix for presence validation of :provider diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 53c3d288f5..ccaf768e09 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true class ApplicationController < ActionController::Base - before_action do - # Unless the request format is explicitly json Rails will mitigate the responsability to CSR to handle it. - render 'components/index' unless valid_api_request? - end + # before_action do + # # Unless the request format is explicitly json Rails will mitigate the responsability to CSR to handle it. + # render 'components/index' unless valid_api_request? + # end # For requests that raised an unkown exception. # Note: The order of each rescue is important (The highest has the lowest priority). @@ -35,6 +35,10 @@ def current_user @current_user ||= User.find_by(id: session[:user_id]) end + def user_avatar(user) + user.avatar.attached? ? url_for(user.avatar) : ActionController::Base.helpers.image_path('default-avatar.png') + end + def render_json(data: {}, errors: [], status: :ok, include: nil) render json: { data:, diff --git a/app/models/room.rb b/app/models/room.rb index 96e0e9e650..3c908f0277 100644 --- a/app/models/room.rb +++ b/app/models/room.rb @@ -2,6 +2,10 @@ class Room < ApplicationRecord belongs_to :user + + has_many :shared_accesses, dependent: :destroy + has_many :shared_users, through: :shared_accesses, class_name: 'User' + has_many :recordings, dependent: :destroy has_many :room_meeting_options, dependent: :destroy @@ -12,6 +16,13 @@ class Room < ApplicationRecord before_validation :set_friendly_id, :set_meeting_id, on: :create after_create :set_meeting_passwords! + def owned_by?(user) + user_id = user&.id + end + + def shared_user + end + private def set_friendly_id diff --git a/app/models/shared_access.rb b/app/models/shared_access.rb new file mode 100644 index 0000000000..05e5226997 --- /dev/null +++ b/app/models/shared_access.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class SharedAccess < ApplicationRecord + belongs_to :shared_user, class_name: 'User', foreign_key: 'user_id', inverse_of: :shared_accesses + belongs_to :shared_room, class_name: 'Room', foreign_key: 'room_id', inverse_of: :shared_accesses +end + diff --git a/app/models/user.rb b/app/models/user.rb index 2eac518199..3cf01724fb 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -5,6 +5,10 @@ class User < ApplicationRecord has_secure_password has_many :rooms, dependent: :destroy + + has_many :shared_accesses, dependent: :destroy + has_many :shared_rooms, through: :shared_accesses, class_name: 'Room' + has_many :recordings, through: :rooms has_one_attached :avatar diff --git a/config/routes.rb b/config/routes.rb index 9ed9b4ab7d..31bb02a70d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -11,7 +11,7 @@ delete 'signout', to: 'sessions#destroy' end end - resources :users, only: %i[create update destroy] do + resources :users, only: %i[index create update destroy] do member do delete :purge_avatar end @@ -19,6 +19,9 @@ resources :rooms, only: %i[show index create destroy], param: :friendly_id do member do post '/start', to: 'rooms#start', as: :start_meeting + post '/shared_access', to: 'rooms#shared_access' + get '/shared_users', to: 'rooms#shared_users' + get '/shareable_users', to: 'rooms#shareable_users' get '/recordings', to: 'rooms#recordings' end end From c48dfb39444d14ee739edbf11f402193def1d9a9 Mon Sep 17 00:00:00 2001 From: scouillard Date: Fri, 6 May 2022 16:09:22 -0400 Subject: [PATCH 03/23] Frontend --- .../stylesheets/application.bootstrap.scss | 15 ++++- app/assets/stylesheets/helpers.scss | 6 +- app/assets/stylesheets/rooms.scss | 9 +++ .../components/forms/CreateRoomForm.jsx | 2 +- .../components/forms/DeleteUserForm.jsx | 25 ++++--- .../components/forms/SharedAccessForm.jsx | 67 +++++++++++++++++++ .../components/rooms/FeatureTabs.jsx | 2 +- app/javascript/components/rooms/RoomsList.jsx | 2 +- .../components/rooms/SharedAccess.jsx | 9 --- .../components/shared/CreateRoomModal.jsx | 32 --------- app/javascript/components/shared/Header.jsx | 2 +- app/javascript/components/shared/Modal.jsx | 17 ++--- .../components/shared/SearchBar.jsx | 21 ++++++ .../components/shared_access/SharedAccess.jsx | 16 +++++ .../shared_access/SharedAccessEmpty.jsx | 30 +++++++++ .../shared_access/SharedAccessList.jsx | 58 ++++++++++++++++ app/javascript/components/users/Avatar.jsx | 7 +- .../components/users/DeleteAccount.jsx | 3 +- app/javascript/components/users/SetAvatar.jsx | 5 +- app/javascript/contexts/auth/AuthProvider.jsx | 2 +- .../hooks/mutations/rooms/useShareAccess.jsx | 28 ++++++++ .../hooks/queries/rooms/useShareableUsers.jsx | 11 +++ .../hooks/queries/rooms/useSharedUsers.jsx | 11 +++ .../queries/{rooms => users}/useSessions.jsx | 0 .../hooks/queries/users/useUsers.jsx | 11 +++ 25 files changed, 313 insertions(+), 78 deletions(-) create mode 100644 app/javascript/components/forms/SharedAccessForm.jsx delete mode 100644 app/javascript/components/rooms/SharedAccess.jsx delete mode 100644 app/javascript/components/shared/CreateRoomModal.jsx create mode 100644 app/javascript/components/shared/SearchBar.jsx create mode 100644 app/javascript/components/shared_access/SharedAccess.jsx create mode 100644 app/javascript/components/shared_access/SharedAccessEmpty.jsx create mode 100644 app/javascript/components/shared_access/SharedAccessList.jsx create mode 100644 app/javascript/hooks/mutations/rooms/useShareAccess.jsx create mode 100644 app/javascript/hooks/queries/rooms/useShareableUsers.jsx create mode 100644 app/javascript/hooks/queries/rooms/useSharedUsers.jsx rename app/javascript/hooks/queries/{rooms => users}/useSessions.jsx (100%) create mode 100644 app/javascript/hooks/queries/users/useUsers.jsx diff --git a/app/assets/stylesheets/application.bootstrap.scss b/app/assets/stylesheets/application.bootstrap.scss index 46979febd8..3bb3a13a4d 100644 --- a/app/assets/stylesheets/application.bootstrap.scss +++ b/app/assets/stylesheets/application.bootstrap.scss @@ -21,7 +21,7 @@ body { } .btn-primary-reverse { - border: 1px solid $primary !important; + border: 2px solid gainsboro !important; color: $primary; background-color: white; @@ -31,3 +31,16 @@ body { } } +//TODO: samuel - need to confirm for Browsers compatibility +input[type='search']::-webkit-search-cancel-button { + -webkit-appearance: none; + cursor: pointer; + height: 20px; + width: 20px; + background-image: url("data:image/svg+xml;utf8,"); +} + +.modal-content { + border: 0; + box-shadow: 0 0.125rem 0.25rem rgb(0 0 0 / 8%) !important; +} diff --git a/app/assets/stylesheets/helpers.scss b/app/assets/stylesheets/helpers.scss index 11948e8d22..fdbc8ea53d 100644 --- a/app/assets/stylesheets/helpers.scss +++ b/app/assets/stylesheets/helpers.scss @@ -19,11 +19,11 @@ } .full-height-rooms { - height: calc(100vh - 171px); + min-height: calc(100vh - 171px); } .full-height-room { - height: calc(100vh - 402px); + min-height: calc(100vh - 402px); } .cursor-pointer { @@ -43,4 +43,4 @@ border-bottom: 1px solid $primary !important; } } -} \ No newline at end of file +} diff --git a/app/assets/stylesheets/rooms.scss b/app/assets/stylesheets/rooms.scss index 0acd5554b3..ef75e358e3 100644 --- a/app/assets/stylesheets/rooms.scss +++ b/app/assets/stylesheets/rooms.scss @@ -27,3 +27,12 @@ background-color: white; border: none; } + +#shared-access-empty { + .user-circle { + width: 100px; + height: 100px; + background-color: $primary-light; + border-radius: 50%; + } +} diff --git a/app/javascript/components/forms/CreateRoomForm.jsx b/app/javascript/components/forms/CreateRoomForm.jsx index c6aaf71723..61922e9bca 100644 --- a/app/javascript/components/forms/CreateRoomForm.jsx +++ b/app/javascript/components/forms/CreateRoomForm.jsx @@ -20,7 +20,7 @@ export default function CreateRoomForm({ handleClose }) {
- - - - + <> +

Are you sure you want to delete your account?

+
+ + + + +
+ ); } diff --git a/app/javascript/components/forms/SharedAccessForm.jsx b/app/javascript/components/forms/SharedAccessForm.jsx new file mode 100644 index 0000000000..41ae7333a8 --- /dev/null +++ b/app/javascript/components/forms/SharedAccessForm.jsx @@ -0,0 +1,67 @@ +import React, { useState } from 'react'; +import { Button, Col, Form, Row, Stack } from 'react-bootstrap'; +import { useForm } from 'react-hook-form'; +import { useParams } from 'react-router-dom'; +import useShareAccess from '../../hooks/mutations/rooms/useShareAccess'; +import Avatar from '../users/Avatar'; +import SearchBar from '../shared/SearchBar'; +import useShareableUsers from '../../hooks/queries/rooms/useShareableUsers'; + +export default function SharedAccessForm({ handleClose }) { + const { register, handleSubmit } = useForm(); + const { friendlyId } = useParams(); + const { onSubmit } = useShareAccess({ friendlyId, closeModal: handleClose }); + const { data: users } = useShareableUsers(friendlyId); + const [search, setSearch] = useState(''); + + return ( + <> + +
+ + + Name + + + Email address + + + { + users?.filter((user) => { + if (user.name.toLowerCase().includes(search.toLowerCase())) { + return user; + } + return false; + }).map((user) => ( + + + + + +
{ user.name }
+
+ + + { user.email } + +
+ )) + } + + + + +
+ + ); +} diff --git a/app/javascript/components/rooms/FeatureTabs.jsx b/app/javascript/components/rooms/FeatureTabs.jsx index e6af3d278b..8c13262881 100644 --- a/app/javascript/components/rooms/FeatureTabs.jsx +++ b/app/javascript/components/rooms/FeatureTabs.jsx @@ -2,7 +2,7 @@ import React from 'react'; import { Row, Tabs, Tab } from 'react-bootstrap'; import RoomRecordings from '../recordings/RoomRecordings'; import Presentation from './Presentation'; -import SharedAccess from './SharedAccess'; +import SharedAccess from '../shared_access/SharedAccess'; import RoomSettings from './RoomSettings'; export default function FeatureTabs() { diff --git a/app/javascript/components/rooms/RoomsList.jsx b/app/javascript/components/rooms/RoomsList.jsx index df69e5cb1a..a71fa12747 100644 --- a/app/javascript/components/rooms/RoomsList.jsx +++ b/app/javascript/components/rooms/RoomsList.jsx @@ -6,7 +6,7 @@ import Spinner from '../shared/stylings/Spinner'; import RoomCard from './RoomCard'; import useRooms from '../../hooks/queries/rooms/useRooms'; import RoomPlaceHolder from './RoomPlaceHolder'; -import CreateRoomModal from '../shared/CreateRoomModal'; +import CreateRoomModal from '../shared/Modal'; import CreateRoomForm from '../forms/CreateRoomForm'; export default function RoomsList() { diff --git a/app/javascript/components/rooms/SharedAccess.jsx b/app/javascript/components/rooms/SharedAccess.jsx deleted file mode 100644 index a7689e8ef2..0000000000 --- a/app/javascript/components/rooms/SharedAccess.jsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react'; - -export default function SharedAccess() { - return ( -
-

Shared Access

-
- ); -} diff --git a/app/javascript/components/shared/CreateRoomModal.jsx b/app/javascript/components/shared/CreateRoomModal.jsx deleted file mode 100644 index cccfb41a95..0000000000 --- a/app/javascript/components/shared/CreateRoomModal.jsx +++ /dev/null @@ -1,32 +0,0 @@ -import React, { useState } from 'react'; -import { Modal as BootstrapModal } from 'react-bootstrap'; -import PropTypes from 'prop-types'; - -export default function Modal({ - modalButton, title, body, -}) { - const [show, setShow] = useState(false); - - const handleClose = () => setShow(false); - const handleShow = () => setShow(true); - - const Body = React.cloneElement(body, { handleClose }); - const ModalButton = React.cloneElement(modalButton, { onClick: handleShow }); - - return ( - <> - {ModalButton} - - - {title} - {Body} - - - ); -} - -Modal.propTypes = { - title: PropTypes.string.isRequired, - modalButton: PropTypes.element.isRequired, - body: PropTypes.node.isRequired, -}; diff --git a/app/javascript/components/shared/Header.jsx b/app/javascript/components/shared/Header.jsx index f4b09ea196..9a60a68e3a 100644 --- a/app/javascript/components/shared/Header.jsx +++ b/app/javascript/components/shared/Header.jsx @@ -25,7 +25,7 @@ export default function Header() {
- + Profile Need help? diff --git a/app/javascript/components/shared/Modal.jsx b/app/javascript/components/shared/Modal.jsx index 29a4c3d1d0..7baf73a856 100644 --- a/app/javascript/components/shared/Modal.jsx +++ b/app/javascript/components/shared/Modal.jsx @@ -3,26 +3,24 @@ import { Modal as BootstrapModal } from 'react-bootstrap'; import PropTypes from 'prop-types'; export default function Modal({ - modalButton, title, body, footer, + modalButton, title, body, }) { const [show, setShow] = useState(false); const handleClose = () => setShow(false); const handleShow = () => setShow(true); - const Footer = React.cloneElement(footer, { handleClose }); + const Body = React.cloneElement(body, { handleClose }); const ModalButton = React.cloneElement(modalButton, { onClick: handleShow }); return ( <> {ModalButton} - - - {title} - {body} - - {Footer} - + + + {title} + + {Body} ); @@ -30,7 +28,6 @@ export default function Modal({ Modal.propTypes = { title: PropTypes.string.isRequired, - footer: PropTypes.node.isRequired, modalButton: PropTypes.element.isRequired, body: PropTypes.node.isRequired, }; diff --git a/app/javascript/components/shared/SearchBar.jsx b/app/javascript/components/shared/SearchBar.jsx new file mode 100644 index 0000000000..d1ba1a7b62 --- /dev/null +++ b/app/javascript/components/shared/SearchBar.jsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { Form } from 'react-bootstrap'; + +export default function SearchBar({ id, setSearch }) { + return ( +
+ + ( + e.key === 'Enter' && e.preventDefault() + )} + onChange={(event) => setSearch(event.target.value)} + /> + +
+ ); +} diff --git a/app/javascript/components/shared_access/SharedAccess.jsx b/app/javascript/components/shared_access/SharedAccess.jsx new file mode 100644 index 0000000000..53803c9069 --- /dev/null +++ b/app/javascript/components/shared_access/SharedAccess.jsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { useParams } from 'react-router-dom'; +import useSharedUsers from '../../hooks/queries/rooms/useSharedUsers'; +import SharedAccessList from './SharedAccessList'; +import SharedAccessEmpty from './SharedAccessEmpty'; + +export default function SharedAccess() { + const { friendlyId } = useParams(); + const { data: users } = useSharedUsers(friendlyId); + + return ( + (users?.length) + ? + : + ); +} diff --git a/app/javascript/components/shared_access/SharedAccessEmpty.jsx b/app/javascript/components/shared_access/SharedAccessEmpty.jsx new file mode 100644 index 0000000000..4052066252 --- /dev/null +++ b/app/javascript/components/shared_access/SharedAccessEmpty.jsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { Button, Card } from 'react-bootstrap'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faUser } from '@fortawesome/free-regular-svg-icons'; +import Modal from '../shared/Modal'; +import SharedAccessForm from '../forms/SharedAccessForm'; + +export default function SharedAccessEmpty() { + return ( +
+ + +
+ +
+ Time to add some users! + + To add new users, click the button below and search or select + the users you want to share this room with. + + + Share Access} + title="Share Room Access" + body={} + /> +
+
+
+ ); +} diff --git a/app/javascript/components/shared_access/SharedAccessList.jsx b/app/javascript/components/shared_access/SharedAccessList.jsx new file mode 100644 index 0000000000..3e8cd59155 --- /dev/null +++ b/app/javascript/components/shared_access/SharedAccessList.jsx @@ -0,0 +1,58 @@ +import React, { useState } from 'react'; +import { Button, Card, Col, Row, Stack } from 'react-bootstrap'; +import Modal from '../shared/Modal'; +import SharedAccessForm from '../forms/SharedAccessForm'; +import Avatar from '../users/Avatar'; +import SearchBar from '../shared/SearchBar'; + +export default function SharedAccessList({ users }) { + const [search, setSearch] = useState(''); + + return ( +
+ + + + Share Access} + title="Share Room Access" + body={} + /> + + + + + + + Name + + + Email address + + + { + users?.filter((user) => { + if (user.name.toLowerCase() + .includes(search.toLowerCase())) { + return user; + } + return false; + }) + .map((user) => ( + + + + +
{user.name}
+
+ + + {user.email} + +
+ )) + } +
+
+
+ ); +} diff --git a/app/javascript/components/users/Avatar.jsx b/app/javascript/components/users/Avatar.jsx index def63f287b..9e0daa7b81 100644 --- a/app/javascript/components/users/Avatar.jsx +++ b/app/javascript/components/users/Avatar.jsx @@ -1,15 +1,14 @@ import React from 'react'; import Image from 'react-bootstrap/Image'; import PropTypes from 'prop-types'; -import { useAuth } from '../../contexts/auth/AuthProvider'; -export default function Avatar({ radius }) { - const currentUser = useAuth(); +export default function Avatar({ avatar, radius }) { return ( - + ); } Avatar.propTypes = { + avatar: PropTypes.string.isRequired, radius: PropTypes.number.isRequired, }; diff --git a/app/javascript/components/users/DeleteAccount.jsx b/app/javascript/components/users/DeleteAccount.jsx index f70517a8f7..51e986dc1c 100644 --- a/app/javascript/components/users/DeleteAccount.jsx +++ b/app/javascript/components/users/DeleteAccount.jsx @@ -15,8 +15,7 @@ export default function DeleteAccount() { Yes, I would like to delete my account} title="Are you sure?" - body="Are you sure you want to delete your account? This action is irreversible." - footer={} + body={} />
); diff --git a/app/javascript/components/users/SetAvatar.jsx b/app/javascript/components/users/SetAvatar.jsx index 871267b443..a46033a204 100644 --- a/app/javascript/components/users/SetAvatar.jsx +++ b/app/javascript/components/users/SetAvatar.jsx @@ -3,11 +3,14 @@ import { Stack } from 'react-bootstrap'; import AvatarForm from '../forms/AvatarForm'; import DeleteAvatarForm from '../forms/DeleteAvatarForm'; import Avatar from './Avatar'; +import { useAuth } from '../../contexts/auth/AuthProvider'; export default function SetAvatar() { + const currentUser = useAuth(); + return ( - + diff --git a/app/javascript/contexts/auth/AuthProvider.jsx b/app/javascript/contexts/auth/AuthProvider.jsx index a80b369402..0d964b87fc 100644 --- a/app/javascript/contexts/auth/AuthProvider.jsx +++ b/app/javascript/contexts/auth/AuthProvider.jsx @@ -1,6 +1,6 @@ import React, { useContext, useMemo } from 'react'; import PropTypes from 'prop-types'; -import useSessions from '../../hooks/queries/rooms/useSessions'; +import useSessions from '../../hooks/queries/users/useSessions'; const AuthContext = React.createContext(); diff --git a/app/javascript/hooks/mutations/rooms/useShareAccess.jsx b/app/javascript/hooks/mutations/rooms/useShareAccess.jsx new file mode 100644 index 0000000000..2afc7b2f09 --- /dev/null +++ b/app/javascript/hooks/mutations/rooms/useShareAccess.jsx @@ -0,0 +1,28 @@ +import { useMutation, useQueryClient } from 'react-query'; +import axios from 'axios'; + +export default function useShareAccess({ friendlyId, closeModal }) { + const queryClient = useQueryClient(); + + const shareAccess = (sharedAccessUsers) => { + axios.post(`/api/v1/rooms/${friendlyId}/shared_access`, sharedAccessUsers); + }; + + const delay = (time) => new Promise((resolve) => { + setTimeout(resolve, time); + }); + + const mutation = useMutation(shareAccess, { + onSuccess: async () => { + closeModal(); + await delay(500); + queryClient.invalidateQueries('getSharedUsers'); + }, + onError: (error) => { + console.log('mutate error', error); + }, + }); + + const onSubmit = (data) => mutation.mutateAsync(data).catch(/* Prevents the promise exception from bubbling */() => {}); + return { onSubmit, ...mutation }; +} diff --git a/app/javascript/hooks/queries/rooms/useShareableUsers.jsx b/app/javascript/hooks/queries/rooms/useShareableUsers.jsx new file mode 100644 index 0000000000..50d7e6f32e --- /dev/null +++ b/app/javascript/hooks/queries/rooms/useShareableUsers.jsx @@ -0,0 +1,11 @@ +import { useQuery } from 'react-query'; +import axios from 'axios'; + +export default function useShareableUsers(friendlyId) { + return useQuery('getShareableUsers', () => axios.get(`/api/v1/rooms/${friendlyId}/shareable_users.json`, { + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + }).then((resp) => resp.data.data)); +} diff --git a/app/javascript/hooks/queries/rooms/useSharedUsers.jsx b/app/javascript/hooks/queries/rooms/useSharedUsers.jsx new file mode 100644 index 0000000000..ba3a546e4b --- /dev/null +++ b/app/javascript/hooks/queries/rooms/useSharedUsers.jsx @@ -0,0 +1,11 @@ +import { useQuery } from 'react-query'; +import axios from 'axios'; + +export default function useSharedUsers(friendlyId) { + return useQuery('getSharedUsers', () => axios.get(`/api/v1/rooms/${friendlyId}/shared_users.json`, { + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + }).then((resp) => resp.data.data)); +} diff --git a/app/javascript/hooks/queries/rooms/useSessions.jsx b/app/javascript/hooks/queries/users/useSessions.jsx similarity index 100% rename from app/javascript/hooks/queries/rooms/useSessions.jsx rename to app/javascript/hooks/queries/users/useSessions.jsx diff --git a/app/javascript/hooks/queries/users/useUsers.jsx b/app/javascript/hooks/queries/users/useUsers.jsx new file mode 100644 index 0000000000..ec2ac05543 --- /dev/null +++ b/app/javascript/hooks/queries/users/useUsers.jsx @@ -0,0 +1,11 @@ +import { useQuery } from 'react-query'; +import axios from 'axios'; + +export default function useUsers() { + return useQuery('getUsers', async () => axios.get('/api/v1/users.json', { + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + }).then((resp) => resp.data.data)); +} From 0fe772b3cd407032f394c43a94227ee0bcab47a5 Mon Sep 17 00:00:00 2001 From: scouillard Date: Fri, 6 May 2022 21:38:12 -0400 Subject: [PATCH 04/23] Add validation on SharedAccess --- app/models/shared_access.rb | 2 ++ db/migrate/20220505192018_create_shared_accesses.rb | 2 ++ db/schema.rb | 1 + 3 files changed, 5 insertions(+) diff --git a/app/models/shared_access.rb b/app/models/shared_access.rb index 05e5226997..28584668eb 100644 --- a/app/models/shared_access.rb +++ b/app/models/shared_access.rb @@ -3,5 +3,7 @@ class SharedAccess < ApplicationRecord belongs_to :shared_user, class_name: 'User', foreign_key: 'user_id', inverse_of: :shared_accesses belongs_to :shared_room, class_name: 'Room', foreign_key: 'room_id', inverse_of: :shared_accesses + + validates :shared_user, uniqueness: { scope: :shared_room } end diff --git a/db/migrate/20220505192018_create_shared_accesses.rb b/db/migrate/20220505192018_create_shared_accesses.rb index ea3f720e59..2b718be6e6 100644 --- a/db/migrate/20220505192018_create_shared_accesses.rb +++ b/db/migrate/20220505192018_create_shared_accesses.rb @@ -4,6 +4,8 @@ def change t.references :user, null: false, foreign_key: true t.references :room, null: false, foreign_key: true + t.index %i[user_id room_id], unique: true + t.timestamps end end diff --git a/db/schema.rb b/db/schema.rb index 31376e0be6..016a320fe9 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -100,6 +100,7 @@ t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["room_id"], name: "index_shared_accesses_on_room_id" + t.index ["user_id", "room_id"], name: "index_shared_accesses_on_user_id_and_room_id", unique: true t.index ["user_id"], name: "index_shared_accesses_on_user_id" end From dde0b04a600d043b06f5decbf29628b203ffe578 Mon Sep 17 00:00:00 2001 From: scouillard Date: Fri, 6 May 2022 22:03:36 -0400 Subject: [PATCH 05/23] Rework SharedAccess POST --- app/controllers/api/v1/rooms_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/v1/rooms_controller.rb b/app/controllers/api/v1/rooms_controller.rb index 2ddbcbe6d5..659413f846 100644 --- a/app/controllers/api/v1/rooms_controller.rb +++ b/app/controllers/api/v1/rooms_controller.rb @@ -70,7 +70,7 @@ def shared_access shared_users = User.where(id: params[:shared_access_users]) shared_users.each do |shared_user| - SharedAccess.where(user_id: shared_user.id, room_id: @room.id).first_or_create + SharedAccess.find_or_create_by!(user_id: shared_user.id, room_id: @room.id) end render_json status: :ok From d186351e9dc05e5abc26281edd2822918bb78f8c Mon Sep 17 00:00:00 2001 From: scouillard Date: Fri, 6 May 2022 22:03:58 -0400 Subject: [PATCH 06/23] Add seeds to create 10 Users --- db/seeds.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/db/seeds.rb b/db/seeds.rb index 0664d1be66..687c98c0fc 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -6,3 +6,16 @@ # # movies = Movie.create([{ name: "Star Wars" }, { name: "Lord of the Rings" }]) # Character.create(name: "Luke", movie: movies.first) +require 'faker' + +10.times do + User.create!( + email: Faker::Internet.email, + name: Faker::Name.name, + password: '12345678', + password_confirmation: '12345678', + provider: 'greenlight', + ) +end + + From 772956181f15a87185eae8d89722e633b196a7a8 Mon Sep 17 00:00:00 2001 From: scouillard Date: Mon, 9 May 2022 13:13:29 -0400 Subject: [PATCH 07/23] Add delete ShareAccess --- .../stylesheets/application.bootstrap.scss | 15 +++++++++ app/controllers/api/v1/rooms_controller.rb | 14 +++++++-- .../forms/DeleteSharedAccessForm.jsx | 22 +++++++++++++ .../shared_access/SharedAccessList.jsx | 2 ++ .../mutations/rooms/useDeleteSharedAccess.jsx | 31 +++++++++++++++++++ .../hooks/mutations/rooms/useShareAccess.jsx | 1 + config/routes.rb | 1 + 7 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 app/javascript/components/forms/DeleteSharedAccessForm.jsx create mode 100644 app/javascript/hooks/mutations/rooms/useDeleteSharedAccess.jsx diff --git a/app/assets/stylesheets/application.bootstrap.scss b/app/assets/stylesheets/application.bootstrap.scss index 3bb3a13a4d..0bad32a0f6 100644 --- a/app/assets/stylesheets/application.bootstrap.scss +++ b/app/assets/stylesheets/application.bootstrap.scss @@ -31,6 +31,21 @@ body { } } +.btn-font-awesome { + background: none; + color: inherit; + border: none; + padding: 0; + font: inherit; + cursor: pointer; + outline: inherit; + + &:focus { + box-shadow: none; + } +} + + //TODO: samuel - need to confirm for Browsers compatibility input[type='search']::-webkit-search-cancel-button { -webkit-appearance: none; diff --git a/app/controllers/api/v1/rooms_controller.rb b/app/controllers/api/v1/rooms_controller.rb index 659413f846..be77376e19 100644 --- a/app/controllers/api/v1/rooms_controller.rb +++ b/app/controllers/api/v1/rooms_controller.rb @@ -4,7 +4,7 @@ module Api module V1 class RoomsController < ApplicationController skip_before_action :verify_authenticity_token # TODO: amir - Revisit this. - before_action :find_room, only: %i[show start recordings shared_access shared_users shareable_users] + before_action :find_room, only: %i[show start recordings shared_access shared_users shareable_users delete_shared_access] # GET /api/v1/rooms.json # Returns: { data: Array[serializable objects(rooms)] , errors: Array[String] } @@ -117,6 +117,16 @@ def shareable_users render_json data: shareable_users, status: :ok end + def delete_shared_access + room = Room.find_by(friendly_id: params[:friendly_id]) + user = User.find_by(id: params[:user_id]) + + SharedAccess.where(user_id: user.id, room_id: room.id).delete_all + + render_json status: :ok + end + + private def find_room @@ -124,7 +134,7 @@ def find_room end def room_params - params.require(:room).permit(:name) + params.require(:room).permit(:name, :user_id) end end end diff --git a/app/javascript/components/forms/DeleteSharedAccessForm.jsx b/app/javascript/components/forms/DeleteSharedAccessForm.jsx new file mode 100644 index 0000000000..697c04203a --- /dev/null +++ b/app/javascript/components/forms/DeleteSharedAccessForm.jsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { useForm } from 'react-hook-form'; +import { Button, Form } from 'react-bootstrap'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faTrashAlt } from '@fortawesome/free-regular-svg-icons'; +import { useParams } from 'react-router-dom'; +import useDeleteSharedAccess from '../../hooks/mutations/rooms/useDeleteSharedAccess'; + +export default function DeleteSharedAccessForm({userId}) { + const { register, handleSubmit } = useForm(); + const { friendlyId } = useParams(); + const { onSubmit } = useDeleteSharedAccess(friendlyId); + + return ( +
+ + +
+ ); +} diff --git a/app/javascript/components/shared_access/SharedAccessList.jsx b/app/javascript/components/shared_access/SharedAccessList.jsx index 3e8cd59155..d8fee63b34 100644 --- a/app/javascript/components/shared_access/SharedAccessList.jsx +++ b/app/javascript/components/shared_access/SharedAccessList.jsx @@ -2,6 +2,7 @@ import React, { useState } from 'react'; import { Button, Card, Col, Row, Stack } from 'react-bootstrap'; import Modal from '../shared/Modal'; import SharedAccessForm from '../forms/SharedAccessForm'; +import DeleteSharedAccessForm from '../forms/DeleteSharedAccessForm'; import Avatar from '../users/Avatar'; import SearchBar from '../shared/SearchBar'; @@ -47,6 +48,7 @@ export default function SharedAccessList({ users }) { {user.email} + )) diff --git a/app/javascript/hooks/mutations/rooms/useDeleteSharedAccess.jsx b/app/javascript/hooks/mutations/rooms/useDeleteSharedAccess.jsx new file mode 100644 index 0000000000..8b243a2735 --- /dev/null +++ b/app/javascript/hooks/mutations/rooms/useDeleteSharedAccess.jsx @@ -0,0 +1,31 @@ +import { useMutation, useQueryClient } from 'react-query'; +import axios from 'axios'; + +export default function useDeleteSharedAccess(friendlyId) { + const queryClient = useQueryClient(); + + const deleteSharedAccess = (data) => { + console.log(data); + axios.post(`/api/v1/rooms/${friendlyId}/delete_shared_access.json`, data); + }; + + const delay = (time) => new Promise((resolve) => { + setTimeout(resolve, time); + }); + + const mutation = useMutation( + deleteSharedAccess, + { + onSuccess: async () => { + await delay(100); + queryClient.invalidateQueries('getSharedUsers'); + }, + onError: (error) => { + console.error('Error:', error.message); + }, + }, + ); + + const onSubmit = (data) => mutation.mutateAsync(data).catch(/* Prevents the promise exception from bubbling */() => {}); + return { onSubmit, ...mutation }; +} diff --git a/app/javascript/hooks/mutations/rooms/useShareAccess.jsx b/app/javascript/hooks/mutations/rooms/useShareAccess.jsx index 2afc7b2f09..1d7edbee5c 100644 --- a/app/javascript/hooks/mutations/rooms/useShareAccess.jsx +++ b/app/javascript/hooks/mutations/rooms/useShareAccess.jsx @@ -5,6 +5,7 @@ export default function useShareAccess({ friendlyId, closeModal }) { const queryClient = useQueryClient(); const shareAccess = (sharedAccessUsers) => { + console.log(sharedAccessUsers); axios.post(`/api/v1/rooms/${friendlyId}/shared_access`, sharedAccessUsers); }; diff --git a/config/routes.rb b/config/routes.rb index 31bb02a70d..e25ac8ab4e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -22,6 +22,7 @@ post '/shared_access', to: 'rooms#shared_access' get '/shared_users', to: 'rooms#shared_users' get '/shareable_users', to: 'rooms#shareable_users' + post '/delete_shared_access', to: 'rooms#delete_shared_access' get '/recordings', to: 'rooms#recordings' end end From 560a6a91a4b4f26e7c6d70b5bfd391b7922afe0c Mon Sep 17 00:00:00 2001 From: scouillard Date: Mon, 9 May 2022 14:25:59 -0400 Subject: [PATCH 08/23] DRY up Rooms controller --- app/controllers/api/v1/rooms_controller.rb | 8 ++++---- .../components/forms/DeleteSharedAccessForm.jsx | 2 +- app/models/room.rb | 7 ------- app/models/user.rb | 8 ++++++++ 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/app/controllers/api/v1/rooms_controller.rb b/app/controllers/api/v1/rooms_controller.rb index be77376e19..e8f882a4b1 100644 --- a/app/controllers/api/v1/rooms_controller.rb +++ b/app/controllers/api/v1/rooms_controller.rb @@ -80,8 +80,9 @@ def shared_access def shared_users shared_users = [] + # User is added to the shared_user list if the room is shared to the user and it is not already included in shared_user User.joins(:shared_rooms).each do |user| - shared_users << user if user.shared_rooms.pluck(:friendly_id).include?(@room.friendly_id) + shared_users << user if user.room_shared?(@room) && shared_users.exclude?(user) end shared_users.map! do |user| @@ -99,10 +100,9 @@ def shared_users def shareable_users shareable_users = [] + # User is added to the shareable_user list unless it's the room owner or the room is already shared to the user User.all.each do |user| - unless user.rooms.pluck(:friendly_id).include?(@room.friendly_id) || user.shared_rooms.pluck(:friendly_id).include?(@room.friendly_id) - shareable_users << user - end + shareable_users << user unless user.room_owner?(@room) || user.room_shared?(@room) end shareable_users.map! do |user| diff --git a/app/javascript/components/forms/DeleteSharedAccessForm.jsx b/app/javascript/components/forms/DeleteSharedAccessForm.jsx index 697c04203a..2c6ec30d7a 100644 --- a/app/javascript/components/forms/DeleteSharedAccessForm.jsx +++ b/app/javascript/components/forms/DeleteSharedAccessForm.jsx @@ -13,7 +13,7 @@ export default function DeleteSharedAccessForm({userId}) { return (
- + diff --git a/app/models/room.rb b/app/models/room.rb index 3c908f0277..f3c45e3bd8 100644 --- a/app/models/room.rb +++ b/app/models/room.rb @@ -16,13 +16,6 @@ class Room < ApplicationRecord before_validation :set_friendly_id, :set_meeting_id, on: :create after_create :set_meeting_passwords! - def owned_by?(user) - user_id = user&.id - end - - def shared_user - end - private def set_friendly_id diff --git a/app/models/user.rb b/app/models/user.rb index 3cf01724fb..e54a3ccfd3 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -22,6 +22,14 @@ class User < ApplicationRecord # TODO: samuel - ActiveStorage validations needs to be discussed and implemented. validate :avatar_validation + def room_owner?(room) + id == room.user_id + end + + def room_shared?(room) + shared_rooms.pluck(:friendly_id).include?(room.friendly_id) + end + private def avatar_validation From a965303ca4643bf714b427741cf9553813df011f Mon Sep 17 00:00:00 2001 From: scouillard Date: Mon, 9 May 2022 17:25:31 -0400 Subject: [PATCH 09/23] Add RSpec --- app/controllers/api/v1/rooms_controller.rb | 12 +++-- .../mutations/rooms/useDeleteSharedAccess.jsx | 3 +- .../hooks/mutations/rooms/useShareAccess.jsx | 3 +- config/routes.rb | 4 +- spec/controllers/rooms_controller_spec.rb | 48 +++++++++++++++++++ 5 files changed, 59 insertions(+), 11 deletions(-) diff --git a/app/controllers/api/v1/rooms_controller.rb b/app/controllers/api/v1/rooms_controller.rb index e8f882a4b1..8a87f3e2b7 100644 --- a/app/controllers/api/v1/rooms_controller.rb +++ b/app/controllers/api/v1/rooms_controller.rb @@ -4,7 +4,7 @@ module Api module V1 class RoomsController < ApplicationController skip_before_action :verify_authenticity_token # TODO: amir - Revisit this. - before_action :find_room, only: %i[show start recordings shared_access shared_users shareable_users delete_shared_access] + before_action :find_room, only: %i[show start recordings share_room_access shared_users shareable_users unshare_room_access] # GET /api/v1/rooms.json # Returns: { data: Array[serializable objects(rooms)] , errors: Array[String] } @@ -65,8 +65,8 @@ def recordings render_json(data: @room.recordings, status: :ok, include: :formats) end - # POST /api/v1/rooms/friendly_id/shared_access - def shared_access + # POST /api/v1/rooms/friendly_id/share_room_access + def share_room_access shared_users = User.where(id: params[:shared_access_users]) shared_users.each do |shared_user| @@ -97,6 +97,7 @@ def shared_users render_json data: shared_users, status: :ok end + # GET /api/v1/rooms/friendly_id/shareable_users.json def shareable_users shareable_users = [] @@ -117,11 +118,12 @@ def shareable_users render_json data: shareable_users, status: :ok end - def delete_shared_access + # DELETE /api/v1/rooms/friendly_id/delete_share_room_access.json + def unshare_room_access room = Room.find_by(friendly_id: params[:friendly_id]) user = User.find_by(id: params[:user_id]) - SharedAccess.where(user_id: user.id, room_id: room.id).delete_all + SharedAccess.find_by!(user_id: user.id, room_id: room.id).delete render_json status: :ok end diff --git a/app/javascript/hooks/mutations/rooms/useDeleteSharedAccess.jsx b/app/javascript/hooks/mutations/rooms/useDeleteSharedAccess.jsx index 8b243a2735..e5b1ba089c 100644 --- a/app/javascript/hooks/mutations/rooms/useDeleteSharedAccess.jsx +++ b/app/javascript/hooks/mutations/rooms/useDeleteSharedAccess.jsx @@ -5,8 +5,7 @@ export default function useDeleteSharedAccess(friendlyId) { const queryClient = useQueryClient(); const deleteSharedAccess = (data) => { - console.log(data); - axios.post(`/api/v1/rooms/${friendlyId}/delete_shared_access.json`, data); + axios.delete(`/api/v1/rooms/${friendlyId}/unshare_room_access.json`, { data }); }; const delay = (time) => new Promise((resolve) => { diff --git a/app/javascript/hooks/mutations/rooms/useShareAccess.jsx b/app/javascript/hooks/mutations/rooms/useShareAccess.jsx index 1d7edbee5c..ab40f2f5b3 100644 --- a/app/javascript/hooks/mutations/rooms/useShareAccess.jsx +++ b/app/javascript/hooks/mutations/rooms/useShareAccess.jsx @@ -5,8 +5,7 @@ export default function useShareAccess({ friendlyId, closeModal }) { const queryClient = useQueryClient(); const shareAccess = (sharedAccessUsers) => { - console.log(sharedAccessUsers); - axios.post(`/api/v1/rooms/${friendlyId}/shared_access`, sharedAccessUsers); + axios.post(`/api/v1/rooms/${friendlyId}/share_room_access`, sharedAccessUsers); }; const delay = (time) => new Promise((resolve) => { diff --git a/config/routes.rb b/config/routes.rb index e25ac8ab4e..3eb2f6620d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -19,10 +19,10 @@ resources :rooms, only: %i[show index create destroy], param: :friendly_id do member do post '/start', to: 'rooms#start', as: :start_meeting - post '/shared_access', to: 'rooms#shared_access' + post '/share_room_access', to: 'rooms#share_room_access' get '/shared_users', to: 'rooms#shared_users' get '/shareable_users', to: 'rooms#shareable_users' - post '/delete_shared_access', to: 'rooms#delete_shared_access' + delete '/unshare_room_access', to: 'rooms#unshare_room_access' get '/recordings', to: 'rooms#recordings' end end diff --git a/spec/controllers/rooms_controller_spec.rb b/spec/controllers/rooms_controller_spec.rb index 4bbb5d9e85..c03b40970f 100644 --- a/spec/controllers/rooms_controller_spec.rb +++ b/spec/controllers/rooms_controller_spec.rb @@ -111,4 +111,52 @@ expect(recording_ids).to be_empty end end + + describe '#share_room_access' do + it 'shares a room with a user' do + room = create(:room) + post :share_room_access, params: { friendly_id: room.friendly_id, shared_access_users: [user.id] } + expect(SharedAccess.exists?(user_id: user.id, room_id: room.id)).to be true + expect(user.shared_rooms).to include(room) + end + + it "doesn't share a room with a user that it not selected" do + room = create(:room) + random_user = create(:user) + post :share_room_access, params: { friendly_id: room.friendly_id, shared_access_users: [random_user.id] } + expect(SharedAccess.exists?(user_id: user.id, room_id: room.id)).to be false + expect(user.shared_rooms).not_to include(room) + end + end + + describe '#unshare_room_access' do + it 'unshares a room with a user' do + room = create(:room) + SharedAccess.create(user_id: user.id, room_id: room.id) + delete :unshare_room_access, params: { friendly_id: room.friendly_id, user_id: user.id, room_id: room.id} + expect(SharedAccess.exists?(user_id: user.id, room_id: room.id)).to be false + expect(user.shared_rooms).not_to include(room) + end + + it "doesn't unshare a room with a user that is not selected" do + room = create(:room) + random_user = create(:user) + SharedAccess.create(user_id: user.id, room_id: room.id) + SharedAccess.create(user_id: random_user.id, room_id: room.id) + delete :unshare_room_access, params: { friendly_id: room.friendly_id, user_id: random_user.id, room_id: room.id} + expect(SharedAccess.exists?(user_id: user.id, room_id: room.id)).to be true + expect(user.shared_rooms).to include(room) + end + end + + # TODO: create list of users instead of a single user + describe '#shared_users' do + it 'lists the users that have been shared the room' do + room = create(:room) + SharedAccess.create(user_id: user.id, room_id: room.id) + get :shared_users, params: { friendly_id: room.friendly_id } + shared_user_response = JSON.parse(response.body)['data'].map { |user| user['id'] } + expect(shared_user_response.first).to eql(user.id) + end + end end From 8a70bd50c450fce5edeb02db9abe63a0d64896f0 Mon Sep 17 00:00:00 2001 From: scouillard Date: Tue, 10 May 2022 14:57:41 -0400 Subject: [PATCH 10/23] Add tests --- app/controllers/api/v1/rooms_controller.rb | 4 +- app/models/user.rb | 5 +++ spec/controllers/rooms_controller_spec.rb | 52 +++++++++++++++++----- spec/factories/shared_access_factory.rb | 8 ++++ 4 files changed, 56 insertions(+), 13 deletions(-) create mode 100644 spec/factories/shared_access_factory.rb diff --git a/app/controllers/api/v1/rooms_controller.rb b/app/controllers/api/v1/rooms_controller.rb index 8a87f3e2b7..a7545b53c1 100644 --- a/app/controllers/api/v1/rooms_controller.rb +++ b/app/controllers/api/v1/rooms_controller.rb @@ -70,7 +70,7 @@ def share_room_access shared_users = User.where(id: params[:shared_access_users]) shared_users.each do |shared_user| - SharedAccess.find_or_create_by!(user_id: shared_user.id, room_id: @room.id) + SharedAccess.find_or_create_by!(user_id: shared_user.id, room_id: @room.id) if shared_user.room_shareable?(@room) end render_json status: :ok @@ -103,7 +103,7 @@ def shareable_users # User is added to the shareable_user list unless it's the room owner or the room is already shared to the user User.all.each do |user| - shareable_users << user unless user.room_owner?(@room) || user.room_shared?(@room) + shareable_users << user if user.room_shareable?(@room) end shareable_users.map! do |user| diff --git a/app/models/user.rb b/app/models/user.rb index e54a3ccfd3..9ebedb8604 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -30,6 +30,11 @@ def room_shared?(room) shared_rooms.pluck(:friendly_id).include?(room.friendly_id) end + # If User is not the room owner, or the room is not being shared already, then the room is shareable to the user. + def room_shareable?(room) + !(room_owner?(room) || room_shared?(room)) + end + private def avatar_validation diff --git a/spec/controllers/rooms_controller_spec.rb b/spec/controllers/rooms_controller_spec.rb index c03b40970f..6e86b62b01 100644 --- a/spec/controllers/rooms_controller_spec.rb +++ b/spec/controllers/rooms_controller_spec.rb @@ -116,7 +116,6 @@ it 'shares a room with a user' do room = create(:room) post :share_room_access, params: { friendly_id: room.friendly_id, shared_access_users: [user.id] } - expect(SharedAccess.exists?(user_id: user.id, room_id: room.id)).to be true expect(user.shared_rooms).to include(room) end @@ -124,7 +123,12 @@ room = create(:room) random_user = create(:user) post :share_room_access, params: { friendly_id: room.friendly_id, shared_access_users: [random_user.id] } - expect(SharedAccess.exists?(user_id: user.id, room_id: room.id)).to be false + expect(user.shared_rooms).not_to include(room) + end + + it "cannot share the room to the room's owner" do + room = create(:room, user:) + post :share_room_access, params: { friendly_id: room.friendly_id, shared_access_users: [user.id] } expect(user.shared_rooms).not_to include(room) end end @@ -132,31 +136,57 @@ describe '#unshare_room_access' do it 'unshares a room with a user' do room = create(:room) - SharedAccess.create(user_id: user.id, room_id: room.id) + create(:shared_access, user_id: user.id, room_id: room.id) delete :unshare_room_access, params: { friendly_id: room.friendly_id, user_id: user.id, room_id: room.id} - expect(SharedAccess.exists?(user_id: user.id, room_id: room.id)).to be false expect(user.shared_rooms).not_to include(room) end it "doesn't unshare a room with a user that is not selected" do room = create(:room) random_user = create(:user) - SharedAccess.create(user_id: user.id, room_id: room.id) - SharedAccess.create(user_id: random_user.id, room_id: room.id) + create(:shared_access, user_id: user.id, room_id: room.id) + create(:shared_access, user_id: random_user.id, room_id: room.id) delete :unshare_room_access, params: { friendly_id: room.friendly_id, user_id: random_user.id, room_id: room.id} - expect(SharedAccess.exists?(user_id: user.id, room_id: room.id)).to be true expect(user.shared_rooms).to include(room) end end - # TODO: create list of users instead of a single user describe '#shared_users' do - it 'lists the users that have been shared the room' do + it 'lists the users that the room has been shared to' do room = create(:room) - SharedAccess.create(user_id: user.id, room_id: room.id) + users = create_list(:user, 10) + shared_users = [] + + users[0..4].each do |user| + create(:shared_access, user_id: user.id, room_id: room.id) + shared_users << user + end + get :shared_users, params: { friendly_id: room.friendly_id } shared_user_response = JSON.parse(response.body)['data'].map { |user| user['id'] } - expect(shared_user_response.first).to eql(user.id) + expect(shared_user_response).to eql(shared_users.pluck(:id)) + end + end + + describe '#shareable_users' do + it 'lists the users that the room can be shared to' do + room = create(:room) + users = create_list(:user, 10) + shared_users = [] + shareable_users = [] + + users[0..4].each do |user| + create(:shared_access, user_id: user.id, room_id: room.id) + shared_users << user + end + + users[5..9].each do |user| + shareable_users << user + end + + get :shareable_users, params: { friendly_id: room.friendly_id } + shareable_user_response = JSON.parse(response.body)['data'].map { |user| user['id'] } + expect(shareable_user_response).to eql(shareable_users.pluck(:id)) end end end diff --git a/spec/factories/shared_access_factory.rb b/spec/factories/shared_access_factory.rb new file mode 100644 index 0000000000..f9e9864263 --- /dev/null +++ b/spec/factories/shared_access_factory.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :shared_access do + user + room + end +end From 3e254a79ca9b0e9f29e26ac622fd4e1419d79bb3 Mon Sep 17 00:00:00 2001 From: scouillard Date: Tue, 10 May 2022 15:57:58 -0400 Subject: [PATCH 11/23] Remove useless line in Spec --- app/models/user.rb | 4 +++- spec/controllers/rooms_controller_spec.rb | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 9ebedb8604..bb18d8de3d 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -32,7 +32,9 @@ def room_shared?(room) # If User is not the room owner, or the room is not being shared already, then the room is shareable to the user. def room_shareable?(room) - !(room_owner?(room) || room_shared?(room)) + return false if room_owner?(room) || room_shared?(room) + + true end private diff --git a/spec/controllers/rooms_controller_spec.rb b/spec/controllers/rooms_controller_spec.rb index 6e86b62b01..68be66add5 100644 --- a/spec/controllers/rooms_controller_spec.rb +++ b/spec/controllers/rooms_controller_spec.rb @@ -172,12 +172,10 @@ it 'lists the users that the room can be shared to' do room = create(:room) users = create_list(:user, 10) - shared_users = [] shareable_users = [] users[0..4].each do |user| create(:shared_access, user_id: user.id, room_id: room.id) - shared_users << user end users[5..9].each do |user| From 300ab6495dfe752b87e6c937de680abbe6c20945 Mon Sep 17 00:00:00 2001 From: scouillard Date: Wed, 11 May 2022 14:47:02 -0400 Subject: [PATCH 12/23] Create ShareAccess controller and routing --- app/controllers/api/v1/rooms_controller.rb | 66 +------------- .../api/v1/shared_accesses_controller.rb | 78 ++++++++++++++++ .../forms/DeleteSharedAccessForm.jsx | 6 +- .../components/forms/SharedAccessForm.jsx | 12 ++- .../components/shared_access/SharedAccess.jsx | 4 +- .../shared_access/SharedAccessList.jsx | 4 +- .../mutations/rooms/useDeleteSharedAccess.jsx | 4 +- .../hooks/mutations/rooms/useShareAccess.jsx | 4 +- .../hooks/queries/rooms/useShareableUsers.jsx | 4 +- .../hooks/queries/rooms/useSharedUsers.jsx | 4 +- config/routes.rb | 11 ++- spec/controllers/rooms_controller_spec.rb | 76 ---------------- .../shared_accesses_controller_spec.rb | 90 +++++++++++++++++++ 13 files changed, 202 insertions(+), 161 deletions(-) create mode 100644 app/controllers/api/v1/shared_accesses_controller.rb create mode 100644 spec/controllers/shared_accesses_controller_spec.rb diff --git a/app/controllers/api/v1/rooms_controller.rb b/app/controllers/api/v1/rooms_controller.rb index a7545b53c1..fb982459f7 100644 --- a/app/controllers/api/v1/rooms_controller.rb +++ b/app/controllers/api/v1/rooms_controller.rb @@ -4,7 +4,7 @@ module Api module V1 class RoomsController < ApplicationController skip_before_action :verify_authenticity_token # TODO: amir - Revisit this. - before_action :find_room, only: %i[show start recordings share_room_access shared_users shareable_users unshare_room_access] + before_action :find_room, only: %i[show start recordings] # GET /api/v1/rooms.json # Returns: { data: Array[serializable objects(rooms)] , errors: Array[String] } @@ -65,70 +65,6 @@ def recordings render_json(data: @room.recordings, status: :ok, include: :formats) end - # POST /api/v1/rooms/friendly_id/share_room_access - def share_room_access - shared_users = User.where(id: params[:shared_access_users]) - - shared_users.each do |shared_user| - SharedAccess.find_or_create_by!(user_id: shared_user.id, room_id: @room.id) if shared_user.room_shareable?(@room) - end - - render_json status: :ok - end - - # GET /api/v1/rooms/friendly_id/shared_users.json - def shared_users - shared_users = [] - - # User is added to the shared_user list if the room is shared to the user and it is not already included in shared_user - User.joins(:shared_rooms).each do |user| - shared_users << user if user.room_shared?(@room) && shared_users.exclude?(user) - end - - shared_users.map! do |user| - { - id: user.id, - name: user.name, - email: user.email, - avatar: user_avatar(user) - } - end - - render_json data: shared_users, status: :ok - end - - # GET /api/v1/rooms/friendly_id/shareable_users.json - def shareable_users - shareable_users = [] - - # User is added to the shareable_user list unless it's the room owner or the room is already shared to the user - User.all.each do |user| - shareable_users << user if user.room_shareable?(@room) - end - - shareable_users.map! do |user| - { - id: user.id, - name: user.name, - email: user.email, - avatar: user_avatar(user) - } - end - - render_json data: shareable_users, status: :ok - end - - # DELETE /api/v1/rooms/friendly_id/delete_share_room_access.json - def unshare_room_access - room = Room.find_by(friendly_id: params[:friendly_id]) - user = User.find_by(id: params[:user_id]) - - SharedAccess.find_by!(user_id: user.id, room_id: room.id).delete - - render_json status: :ok - end - - private def find_room diff --git a/app/controllers/api/v1/shared_accesses_controller.rb b/app/controllers/api/v1/shared_accesses_controller.rb new file mode 100644 index 0000000000..279126fef7 --- /dev/null +++ b/app/controllers/api/v1/shared_accesses_controller.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +module Api + module V1 + class SharedAccessesController < ApplicationController + skip_before_action :verify_authenticity_token + before_action :find_room + + # POST /api/v1/shared_accesses/room_id + def create + users = User.where(id: params[:users]) + + users.each do |user| + SharedAccess.find_or_create_by!(user_id: user.id, room_id: @room.id) if user.room_shareable?(@room) + end + + render_json status: :ok + end + + # DELETE /api/v1/shared_accesses/room_id + def destroy + user = User.find_by(id: params[:user_id]) + + SharedAccess.find_by!(user_id: user.id, room_id: @room.id).delete + + render_json status: :ok + end + + # GET /api/v1/shared_accesses/room_id/shared_users.json + def shared_users + shared_users = [] + + # User is added to the shared_user list if the room is shared to the user and it is not already included in shared_user + User.joins(:shared_rooms).each do |user| + shared_users << user if user.room_shared?(@room) && shared_users.exclude?(user) + end + + shared_users.map! do |user| + { + id: user.id, + name: user.name, + email: user.email, + avatar: user_avatar(user) + } + end + + render_json data: shared_users, status: :ok + end + + # GET /api/v1/shared_accesses/room_id/shareable_users.json + def shareable_users + shareable_users = [] + + # User is added to the shareable_user list unless it's the room owner or the room is already shared to the user + User.all.each do |user| + shareable_users << user if user.room_shareable?(@room) + end + + shareable_users.map! do |user| + { + id: user.id, + name: user.name, + email: user.email, + avatar: user_avatar(user) + } + end + + render_json data: shareable_users, status: :ok + end + + private + + def find_room + @room = Room.find_by(id: params[:room_id]) + end + end + end +end diff --git a/app/javascript/components/forms/DeleteSharedAccessForm.jsx b/app/javascript/components/forms/DeleteSharedAccessForm.jsx index 2c6ec30d7a..fbaca91942 100644 --- a/app/javascript/components/forms/DeleteSharedAccessForm.jsx +++ b/app/javascript/components/forms/DeleteSharedAccessForm.jsx @@ -5,11 +5,13 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faTrashAlt } from '@fortawesome/free-regular-svg-icons'; import { useParams } from 'react-router-dom'; import useDeleteSharedAccess from '../../hooks/mutations/rooms/useDeleteSharedAccess'; +import useRoom from '../../hooks/queries/rooms/useRoom'; -export default function DeleteSharedAccessForm({userId}) { +export default function DeleteSharedAccessForm({ userId }) { const { register, handleSubmit } = useForm(); const { friendlyId } = useParams(); - const { onSubmit } = useDeleteSharedAccess(friendlyId); + const { data: room } = useRoom(friendlyId); + const { onSubmit } = useDeleteSharedAccess(room.id); return ( diff --git a/app/javascript/components/forms/SharedAccessForm.jsx b/app/javascript/components/forms/SharedAccessForm.jsx index 41ae7333a8..17a2b26bfd 100644 --- a/app/javascript/components/forms/SharedAccessForm.jsx +++ b/app/javascript/components/forms/SharedAccessForm.jsx @@ -1,17 +1,21 @@ import React, { useState } from 'react'; -import { Button, Col, Form, Row, Stack } from 'react-bootstrap'; +import { + Button, Col, Form, Row, Stack, +} from 'react-bootstrap'; import { useForm } from 'react-hook-form'; import { useParams } from 'react-router-dom'; import useShareAccess from '../../hooks/mutations/rooms/useShareAccess'; import Avatar from '../users/Avatar'; import SearchBar from '../shared/SearchBar'; import useShareableUsers from '../../hooks/queries/rooms/useShareableUsers'; +import useRoom from '../../hooks/queries/rooms/useRoom'; export default function SharedAccessForm({ handleClose }) { const { register, handleSubmit } = useForm(); const { friendlyId } = useParams(); - const { onSubmit } = useShareAccess({ friendlyId, closeModal: handleClose }); - const { data: users } = useShareableUsers(friendlyId); + const { isLoading, data: room } = useRoom(friendlyId); + const { onSubmit } = useShareAccess({ roomId: room.id, closeModal: handleClose }); + const { data: users } = useShareableUsers(room.id); const [search, setSearch] = useState(''); return ( @@ -41,7 +45,7 @@ export default function SharedAccessForm({ handleClose }) { value={user.id} aria-label="tbd" className="pe-3" - {...register('shared_access_users')} + {...register('users')} />
{ user.name }
diff --git a/app/javascript/components/shared_access/SharedAccess.jsx b/app/javascript/components/shared_access/SharedAccess.jsx index 53803c9069..2b6cd39b71 100644 --- a/app/javascript/components/shared_access/SharedAccess.jsx +++ b/app/javascript/components/shared_access/SharedAccess.jsx @@ -3,10 +3,12 @@ import { useParams } from 'react-router-dom'; import useSharedUsers from '../../hooks/queries/rooms/useSharedUsers'; import SharedAccessList from './SharedAccessList'; import SharedAccessEmpty from './SharedAccessEmpty'; +import useRoom from '../../hooks/queries/rooms/useRoom'; export default function SharedAccess() { const { friendlyId } = useParams(); - const { data: users } = useSharedUsers(friendlyId); + const { isLoading, data: room } = useRoom(friendlyId); + const { data: users } = useSharedUsers(room.id); return ( (users?.length) diff --git a/app/javascript/components/shared_access/SharedAccessList.jsx b/app/javascript/components/shared_access/SharedAccessList.jsx index d8fee63b34..5683ee13dc 100644 --- a/app/javascript/components/shared_access/SharedAccessList.jsx +++ b/app/javascript/components/shared_access/SharedAccessList.jsx @@ -1,5 +1,7 @@ import React, { useState } from 'react'; -import { Button, Card, Col, Row, Stack } from 'react-bootstrap'; +import { + Button, Card, Col, Row, Stack, +} from 'react-bootstrap'; import Modal from '../shared/Modal'; import SharedAccessForm from '../forms/SharedAccessForm'; import DeleteSharedAccessForm from '../forms/DeleteSharedAccessForm'; diff --git a/app/javascript/hooks/mutations/rooms/useDeleteSharedAccess.jsx b/app/javascript/hooks/mutations/rooms/useDeleteSharedAccess.jsx index e5b1ba089c..04bb4b4b2b 100644 --- a/app/javascript/hooks/mutations/rooms/useDeleteSharedAccess.jsx +++ b/app/javascript/hooks/mutations/rooms/useDeleteSharedAccess.jsx @@ -1,11 +1,11 @@ import { useMutation, useQueryClient } from 'react-query'; import axios from 'axios'; -export default function useDeleteSharedAccess(friendlyId) { +export default function useDeleteSharedAccess(roomId) { const queryClient = useQueryClient(); const deleteSharedAccess = (data) => { - axios.delete(`/api/v1/rooms/${friendlyId}/unshare_room_access.json`, { data }); + axios.delete(`/api/v1/shared_accesses/${roomId}`, { data }); }; const delay = (time) => new Promise((resolve) => { diff --git a/app/javascript/hooks/mutations/rooms/useShareAccess.jsx b/app/javascript/hooks/mutations/rooms/useShareAccess.jsx index ab40f2f5b3..24dd785394 100644 --- a/app/javascript/hooks/mutations/rooms/useShareAccess.jsx +++ b/app/javascript/hooks/mutations/rooms/useShareAccess.jsx @@ -1,11 +1,11 @@ import { useMutation, useQueryClient } from 'react-query'; import axios from 'axios'; -export default function useShareAccess({ friendlyId, closeModal }) { +export default function useShareAccess({ roomId, closeModal }) { const queryClient = useQueryClient(); const shareAccess = (sharedAccessUsers) => { - axios.post(`/api/v1/rooms/${friendlyId}/share_room_access`, sharedAccessUsers); + axios.post(`/api/v1/shared_accesses/${roomId}`, sharedAccessUsers); }; const delay = (time) => new Promise((resolve) => { diff --git a/app/javascript/hooks/queries/rooms/useShareableUsers.jsx b/app/javascript/hooks/queries/rooms/useShareableUsers.jsx index 50d7e6f32e..2bd2b469f8 100644 --- a/app/javascript/hooks/queries/rooms/useShareableUsers.jsx +++ b/app/javascript/hooks/queries/rooms/useShareableUsers.jsx @@ -1,8 +1,8 @@ import { useQuery } from 'react-query'; import axios from 'axios'; -export default function useShareableUsers(friendlyId) { - return useQuery('getShareableUsers', () => axios.get(`/api/v1/rooms/${friendlyId}/shareable_users.json`, { +export default function useShareableUsers(roomId) { + return useQuery('getShareableUsers', () => axios.get(`/api/v1/shared_accesses/${roomId}/shareable_users.json`, { headers: { 'Content-Type': 'application/json', Accept: 'application/json', diff --git a/app/javascript/hooks/queries/rooms/useSharedUsers.jsx b/app/javascript/hooks/queries/rooms/useSharedUsers.jsx index ba3a546e4b..fa441be5df 100644 --- a/app/javascript/hooks/queries/rooms/useSharedUsers.jsx +++ b/app/javascript/hooks/queries/rooms/useSharedUsers.jsx @@ -1,8 +1,8 @@ import { useQuery } from 'react-query'; import axios from 'axios'; -export default function useSharedUsers(friendlyId) { - return useQuery('getSharedUsers', () => axios.get(`/api/v1/rooms/${friendlyId}/shared_users.json`, { +export default function useSharedUsers(roomId) { + return useQuery('getSharedUsers', () => axios.get(`/api/v1/shared_accesses/${roomId}/shared_users.json`, { headers: { 'Content-Type': 'application/json', Accept: 'application/json', diff --git a/config/routes.rb b/config/routes.rb index 3eb2f6620d..b33663cec8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -19,13 +19,16 @@ resources :rooms, only: %i[show index create destroy], param: :friendly_id do member do post '/start', to: 'rooms#start', as: :start_meeting - post '/share_room_access', to: 'rooms#share_room_access' - get '/shared_users', to: 'rooms#shared_users' - get '/shareable_users', to: 'rooms#shareable_users' - delete '/unshare_room_access', to: 'rooms#unshare_room_access' get '/recordings', to: 'rooms#recordings' end end + resources :shared_accesses, only: :destroy, param: :room_id do + member do + post :create + get '/shared_users', to: 'shared_accesses#shared_users' + get '/shareable_users', to: 'shared_accesses#shareable_users' + end + end resources :recordings, only: [:index] end end diff --git a/spec/controllers/rooms_controller_spec.rb b/spec/controllers/rooms_controller_spec.rb index 68be66add5..4bbb5d9e85 100644 --- a/spec/controllers/rooms_controller_spec.rb +++ b/spec/controllers/rooms_controller_spec.rb @@ -111,80 +111,4 @@ expect(recording_ids).to be_empty end end - - describe '#share_room_access' do - it 'shares a room with a user' do - room = create(:room) - post :share_room_access, params: { friendly_id: room.friendly_id, shared_access_users: [user.id] } - expect(user.shared_rooms).to include(room) - end - - it "doesn't share a room with a user that it not selected" do - room = create(:room) - random_user = create(:user) - post :share_room_access, params: { friendly_id: room.friendly_id, shared_access_users: [random_user.id] } - expect(user.shared_rooms).not_to include(room) - end - - it "cannot share the room to the room's owner" do - room = create(:room, user:) - post :share_room_access, params: { friendly_id: room.friendly_id, shared_access_users: [user.id] } - expect(user.shared_rooms).not_to include(room) - end - end - - describe '#unshare_room_access' do - it 'unshares a room with a user' do - room = create(:room) - create(:shared_access, user_id: user.id, room_id: room.id) - delete :unshare_room_access, params: { friendly_id: room.friendly_id, user_id: user.id, room_id: room.id} - expect(user.shared_rooms).not_to include(room) - end - - it "doesn't unshare a room with a user that is not selected" do - room = create(:room) - random_user = create(:user) - create(:shared_access, user_id: user.id, room_id: room.id) - create(:shared_access, user_id: random_user.id, room_id: room.id) - delete :unshare_room_access, params: { friendly_id: room.friendly_id, user_id: random_user.id, room_id: room.id} - expect(user.shared_rooms).to include(room) - end - end - - describe '#shared_users' do - it 'lists the users that the room has been shared to' do - room = create(:room) - users = create_list(:user, 10) - shared_users = [] - - users[0..4].each do |user| - create(:shared_access, user_id: user.id, room_id: room.id) - shared_users << user - end - - get :shared_users, params: { friendly_id: room.friendly_id } - shared_user_response = JSON.parse(response.body)['data'].map { |user| user['id'] } - expect(shared_user_response).to eql(shared_users.pluck(:id)) - end - end - - describe '#shareable_users' do - it 'lists the users that the room can be shared to' do - room = create(:room) - users = create_list(:user, 10) - shareable_users = [] - - users[0..4].each do |user| - create(:shared_access, user_id: user.id, room_id: room.id) - end - - users[5..9].each do |user| - shareable_users << user - end - - get :shareable_users, params: { friendly_id: room.friendly_id } - shareable_user_response = JSON.parse(response.body)['data'].map { |user| user['id'] } - expect(shareable_user_response).to eql(shareable_users.pluck(:id)) - end - end end diff --git a/spec/controllers/shared_accesses_controller_spec.rb b/spec/controllers/shared_accesses_controller_spec.rb new file mode 100644 index 0000000000..258956756f --- /dev/null +++ b/spec/controllers/shared_accesses_controller_spec.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Api::V1::SharedAccessesController, type: :controller do + before do + request.headers['ACCEPT'] = 'application/json' + end + + describe '#create' do + it 'shares a room with a user' do + room = create(:room) + user = create(:user) + post :create, params: { room_id: room.id, users: [user.id] } + expect(user.shared_rooms).to include(room) + end + + it "doesn't share a room with a user that it not selected" do + room = create(:room) + user = create(:user) + random_user = create(:user) + post :create, params: { room_id: room.id, users: [random_user.id] } + expect(user.shared_rooms).not_to include(room) + end + + it "cannot share the room to the room's owner" do + user = create(:user) + room = create(:room, user:) + post :create, params: { room_id: room.id, users: [user.id] } + expect(user.shared_rooms).not_to include(room) + end + end + + describe '#destroy' do + it 'unshares a room with a user' do + room = create(:room) + user = create(:user) + create(:shared_access, user_id: user.id, room_id: room.id) + delete :destroy, params: { room_id: room.id, user_id: user.id} + expect(user.shared_rooms).not_to include(room) + end + + it "doesn't unshare a room with a user that is not selected" do + room = create(:room) + user = create(:user) + random_user = create(:user) + create(:shared_access, user_id: user.id, room_id: room.id) + create(:shared_access, user_id: random_user.id, room_id: room.id) + delete :destroy, params: { room_id: room.id, user_id: random_user.id} + expect(user.shared_rooms).to include(room) + end + end + + describe '#shared_users' do + it 'lists the users that the room has been shared to' do + room = create(:room) + users = create_list(:user, 10) + shared_users = [] + + users[0..4].each do |user| + create(:shared_access, user_id: user.id, room_id: room.id) + shared_users << user + end + + get :shared_users, params: { room_id: room.id } + shared_user_response = JSON.parse(response.body)['data'].map { |user| user['id'] } + expect(shared_user_response).to eql(shared_users.pluck(:id)) + end + end + + describe '#shareable_users' do + it 'lists the users that the room can be shared to' do + room = create(:room) + users = create_list(:user, 10) + shareable_users = [] + + users[0..4].each do |user| + create(:shared_access, user_id: user.id, room_id: room.id) + end + + users[5..9].each do |user| + shareable_users << user + end + + get :shareable_users, params: { room_id: room.id } + shareable_user_response = JSON.parse(response.body)['data'].map { |user| user['id'] } + expect(shareable_user_response).to eql(shareable_users.pluck(:id)) + end + end +end From 9570e0b3ff7abc5b0f9b07c6ac389758272aa6f9 Mon Sep 17 00:00:00 2001 From: scouillard Date: Wed, 11 May 2022 15:20:10 -0400 Subject: [PATCH 13/23] Fix esf and Rubocop --- .../components/forms/DeleteSharedAccessForm.jsx | 9 ++++++++- .../components/forms/SharedAccessForm.jsx | 17 ++++++++++++++--- app/javascript/components/shared/SearchBar.jsx | 6 ++++++ .../components/shared_access/SharedAccess.jsx | 4 ++-- .../shared_access/SharedAccessList.jsx | 11 +++++++++++ .../useDeleteSharedAccess.jsx | 0 .../useShareAccess.jsx | 0 .../useShareableUsers.jsx | 0 .../useSharedUsers.jsx | 0 9 files changed, 41 insertions(+), 6 deletions(-) rename app/javascript/hooks/mutations/{rooms => shared_accesses}/useDeleteSharedAccess.jsx (100%) rename app/javascript/hooks/mutations/{rooms => shared_accesses}/useShareAccess.jsx (100%) rename app/javascript/hooks/queries/{rooms => shared_accesses}/useShareableUsers.jsx (100%) rename app/javascript/hooks/queries/{rooms => shared_accesses}/useSharedUsers.jsx (100%) diff --git a/app/javascript/components/forms/DeleteSharedAccessForm.jsx b/app/javascript/components/forms/DeleteSharedAccessForm.jsx index fbaca91942..81b82e6f41 100644 --- a/app/javascript/components/forms/DeleteSharedAccessForm.jsx +++ b/app/javascript/components/forms/DeleteSharedAccessForm.jsx @@ -1,10 +1,13 @@ +/* eslint-disable react/jsx-props-no-spreading */ + import React from 'react'; import { useForm } from 'react-hook-form'; import { Button, Form } from 'react-bootstrap'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faTrashAlt } from '@fortawesome/free-regular-svg-icons'; import { useParams } from 'react-router-dom'; -import useDeleteSharedAccess from '../../hooks/mutations/rooms/useDeleteSharedAccess'; +import PropTypes from 'prop-types'; +import useDeleteSharedAccess from '../../hooks/mutations/shared_accesses/useDeleteSharedAccess'; import useRoom from '../../hooks/queries/rooms/useRoom'; export default function DeleteSharedAccessForm({ userId }) { @@ -22,3 +25,7 @@ export default function DeleteSharedAccessForm({ userId }) { ); } + +DeleteSharedAccessForm.propTypes = { + userId: PropTypes.number.isRequired, +}; diff --git a/app/javascript/components/forms/SharedAccessForm.jsx b/app/javascript/components/forms/SharedAccessForm.jsx index 17a2b26bfd..7e43cadb3e 100644 --- a/app/javascript/components/forms/SharedAccessForm.jsx +++ b/app/javascript/components/forms/SharedAccessForm.jsx @@ -1,19 +1,22 @@ +/* eslint-disable react/jsx-props-no-spreading */ + import React, { useState } from 'react'; import { Button, Col, Form, Row, Stack, } from 'react-bootstrap'; import { useForm } from 'react-hook-form'; import { useParams } from 'react-router-dom'; -import useShareAccess from '../../hooks/mutations/rooms/useShareAccess'; +import PropTypes from 'prop-types'; +import useShareAccess from '../../hooks/mutations/shared_accesses/useShareAccess'; import Avatar from '../users/Avatar'; import SearchBar from '../shared/SearchBar'; -import useShareableUsers from '../../hooks/queries/rooms/useShareableUsers'; +import useShareableUsers from '../../hooks/queries/shared_accesses/useShareableUsers'; import useRoom from '../../hooks/queries/rooms/useRoom'; export default function SharedAccessForm({ handleClose }) { const { register, handleSubmit } = useForm(); const { friendlyId } = useParams(); - const { isLoading, data: room } = useRoom(friendlyId); + const { data: room } = useRoom(friendlyId); const { onSubmit } = useShareAccess({ roomId: room.id, closeModal: handleClose }); const { data: users } = useShareableUsers(room.id); const [search, setSearch] = useState(''); @@ -69,3 +72,11 @@ export default function SharedAccessForm({ handleClose }) { ); } + +SharedAccessForm.propTypes = { + handleClose: PropTypes.func, +}; + +SharedAccessForm.defaultProps = { + handleClose: () => { }, +}; diff --git a/app/javascript/components/shared/SearchBar.jsx b/app/javascript/components/shared/SearchBar.jsx index d1ba1a7b62..688882bc53 100644 --- a/app/javascript/components/shared/SearchBar.jsx +++ b/app/javascript/components/shared/SearchBar.jsx @@ -1,5 +1,6 @@ import React from 'react'; import { Form } from 'react-bootstrap'; +import PropTypes from 'prop-types'; export default function SearchBar({ id, setSearch }) { return ( @@ -19,3 +20,8 @@ export default function SearchBar({ id, setSearch }) { ); } + +SearchBar.propTypes = { + id: PropTypes.number.isRequired, + setSearch: PropTypes.func.isRequired, +}; diff --git a/app/javascript/components/shared_access/SharedAccess.jsx b/app/javascript/components/shared_access/SharedAccess.jsx index 2b6cd39b71..74572299dc 100644 --- a/app/javascript/components/shared_access/SharedAccess.jsx +++ b/app/javascript/components/shared_access/SharedAccess.jsx @@ -1,13 +1,13 @@ import React from 'react'; import { useParams } from 'react-router-dom'; -import useSharedUsers from '../../hooks/queries/rooms/useSharedUsers'; +import useSharedUsers from '../../hooks/queries/shared_accesses/useSharedUsers'; import SharedAccessList from './SharedAccessList'; import SharedAccessEmpty from './SharedAccessEmpty'; import useRoom from '../../hooks/queries/rooms/useRoom'; export default function SharedAccess() { const { friendlyId } = useParams(); - const { isLoading, data: room } = useRoom(friendlyId); + const { data: room } = useRoom(friendlyId); const { data: users } = useSharedUsers(room.id); return ( diff --git a/app/javascript/components/shared_access/SharedAccessList.jsx b/app/javascript/components/shared_access/SharedAccessList.jsx index 5683ee13dc..9c5ef34b70 100644 --- a/app/javascript/components/shared_access/SharedAccessList.jsx +++ b/app/javascript/components/shared_access/SharedAccessList.jsx @@ -2,6 +2,7 @@ import React, { useState } from 'react'; import { Button, Card, Col, Row, Stack, } from 'react-bootstrap'; +import PropTypes from 'prop-types'; import Modal from '../shared/Modal'; import SharedAccessForm from '../forms/SharedAccessForm'; import DeleteSharedAccessForm from '../forms/DeleteSharedAccessForm'; @@ -60,3 +61,13 @@ export default function SharedAccessList({ users }) { ); } + +SharedAccessList.propTypes = { + users: PropTypes.shape({ + id: PropTypes.number.isRequired, + name: PropTypes.string.isRequired, + email: PropTypes.string.isRequired, + avatar: PropTypes.string.isRequired, + filter: PropTypes.func, + }).isRequired, +}; diff --git a/app/javascript/hooks/mutations/rooms/useDeleteSharedAccess.jsx b/app/javascript/hooks/mutations/shared_accesses/useDeleteSharedAccess.jsx similarity index 100% rename from app/javascript/hooks/mutations/rooms/useDeleteSharedAccess.jsx rename to app/javascript/hooks/mutations/shared_accesses/useDeleteSharedAccess.jsx diff --git a/app/javascript/hooks/mutations/rooms/useShareAccess.jsx b/app/javascript/hooks/mutations/shared_accesses/useShareAccess.jsx similarity index 100% rename from app/javascript/hooks/mutations/rooms/useShareAccess.jsx rename to app/javascript/hooks/mutations/shared_accesses/useShareAccess.jsx diff --git a/app/javascript/hooks/queries/rooms/useShareableUsers.jsx b/app/javascript/hooks/queries/shared_accesses/useShareableUsers.jsx similarity index 100% rename from app/javascript/hooks/queries/rooms/useShareableUsers.jsx rename to app/javascript/hooks/queries/shared_accesses/useShareableUsers.jsx diff --git a/app/javascript/hooks/queries/rooms/useSharedUsers.jsx b/app/javascript/hooks/queries/shared_accesses/useSharedUsers.jsx similarity index 100% rename from app/javascript/hooks/queries/rooms/useSharedUsers.jsx rename to app/javascript/hooks/queries/shared_accesses/useSharedUsers.jsx From ee6e74d26d82fcf8a4ed32cdc6bea8a30071aa38 Mon Sep 17 00:00:00 2001 From: scouillard Date: Wed, 11 May 2022 16:35:24 -0400 Subject: [PATCH 14/23] Add RoomContext to SharedAccess --- .../components/forms/DeleteSharedAccessForm.jsx | 8 +++----- app/javascript/components/forms/SharedAccessForm.jsx | 8 +++----- .../components/shared_access/SharedAccess.jsx | 12 +++++++++--- app/javascript/contexts/roomContext.jsx | 7 +++++++ 4 files changed, 22 insertions(+), 13 deletions(-) create mode 100644 app/javascript/contexts/roomContext.jsx diff --git a/app/javascript/components/forms/DeleteSharedAccessForm.jsx b/app/javascript/components/forms/DeleteSharedAccessForm.jsx index 81b82e6f41..cd46e45cc8 100644 --- a/app/javascript/components/forms/DeleteSharedAccessForm.jsx +++ b/app/javascript/components/forms/DeleteSharedAccessForm.jsx @@ -1,19 +1,17 @@ /* eslint-disable react/jsx-props-no-spreading */ -import React from 'react'; +import React, { useContext } from 'react'; import { useForm } from 'react-hook-form'; import { Button, Form } from 'react-bootstrap'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faTrashAlt } from '@fortawesome/free-regular-svg-icons'; -import { useParams } from 'react-router-dom'; import PropTypes from 'prop-types'; import useDeleteSharedAccess from '../../hooks/mutations/shared_accesses/useDeleteSharedAccess'; -import useRoom from '../../hooks/queries/rooms/useRoom'; +import RoomContext from '../../contexts/roomContext'; export default function DeleteSharedAccessForm({ userId }) { const { register, handleSubmit } = useForm(); - const { friendlyId } = useParams(); - const { data: room } = useRoom(friendlyId); + const room = useContext(RoomContext); const { onSubmit } = useDeleteSharedAccess(room.id); return ( diff --git a/app/javascript/components/forms/SharedAccessForm.jsx b/app/javascript/components/forms/SharedAccessForm.jsx index 7e43cadb3e..dedb433148 100644 --- a/app/javascript/components/forms/SharedAccessForm.jsx +++ b/app/javascript/components/forms/SharedAccessForm.jsx @@ -1,22 +1,20 @@ /* eslint-disable react/jsx-props-no-spreading */ -import React, { useState } from 'react'; +import React, { useContext, useState } from 'react'; import { Button, Col, Form, Row, Stack, } from 'react-bootstrap'; import { useForm } from 'react-hook-form'; -import { useParams } from 'react-router-dom'; import PropTypes from 'prop-types'; import useShareAccess from '../../hooks/mutations/shared_accesses/useShareAccess'; import Avatar from '../users/Avatar'; import SearchBar from '../shared/SearchBar'; import useShareableUsers from '../../hooks/queries/shared_accesses/useShareableUsers'; -import useRoom from '../../hooks/queries/rooms/useRoom'; +import RoomContext from '../../contexts/roomContext'; export default function SharedAccessForm({ handleClose }) { const { register, handleSubmit } = useForm(); - const { friendlyId } = useParams(); - const { data: room } = useRoom(friendlyId); + const room = useContext(RoomContext); const { onSubmit } = useShareAccess({ roomId: room.id, closeModal: handleClose }); const { data: users } = useShareableUsers(room.id); const [search, setSearch] = useState(''); diff --git a/app/javascript/components/shared_access/SharedAccess.jsx b/app/javascript/components/shared_access/SharedAccess.jsx index 74572299dc..4ccb019109 100644 --- a/app/javascript/components/shared_access/SharedAccess.jsx +++ b/app/javascript/components/shared_access/SharedAccess.jsx @@ -4,15 +4,21 @@ import useSharedUsers from '../../hooks/queries/shared_accesses/useSharedUsers'; import SharedAccessList from './SharedAccessList'; import SharedAccessEmpty from './SharedAccessEmpty'; import useRoom from '../../hooks/queries/rooms/useRoom'; +import { RoomProvider } from '../../contexts/roomContext'; export default function SharedAccess() { const { friendlyId } = useParams(); + // TODO: samuel - roomContext could/should be used higher up the tree (in Rooms/Room) const { data: room } = useRoom(friendlyId); const { data: users } = useSharedUsers(room.id); return ( - (users?.length) - ? - : + + { + (users?.length) + ? + : + } + ); } diff --git a/app/javascript/contexts/roomContext.jsx b/app/javascript/contexts/roomContext.jsx new file mode 100644 index 0000000000..4c8936cb8d --- /dev/null +++ b/app/javascript/contexts/roomContext.jsx @@ -0,0 +1,7 @@ +import React from 'react'; + +const RoomContext = React.createContext({}); + +export const RoomProvider = RoomContext.Provider; + +export default RoomContext; From d992d5e6dec3b84ceece2edbcb805af1b40f8450 Mon Sep 17 00:00:00 2001 From: scouillard Date: Wed, 11 May 2022 17:05:04 -0400 Subject: [PATCH 15/23] Style SearchBar --- .../stylesheets/application.bootstrap.scss | 17 ++++++++++++----- app/javascript/components/shared/SearchBar.jsx | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/application.bootstrap.scss b/app/assets/stylesheets/application.bootstrap.scss index 0bad32a0f6..78dc667ee2 100644 --- a/app/assets/stylesheets/application.bootstrap.scss +++ b/app/assets/stylesheets/application.bootstrap.scss @@ -45,17 +45,24 @@ body { } } +.modal-content { + border: 0; + box-shadow: 0 0.125rem 0.25rem rgb(0 0 0 / 8%) !important; +} -//TODO: samuel - need to confirm for Browsers compatibility +//TODO: samuel - need to confirm for Browsers compatibility (maybe Scope this a bit more?) input[type='search']::-webkit-search-cancel-button { -webkit-appearance: none; cursor: pointer; height: 20px; width: 20px; - background-image: url("data:image/svg+xml;utf8,"); + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' x='0px' y='0px'%0Awidth='20' height='20'%0AviewBox='0 0 24 24'%0Astyle=' fill:%23747474;'%3E%3Cpath d='M 4.9902344 3.9902344 A 1.0001 1.0001 0 0 0 4.2929688 5.7070312 L 10.585938 12 L 4.2929688 18.292969 A 1.0001 1.0001 0 1 0 5.7070312 19.707031 L 12 13.414062 L 18.292969 19.707031 A 1.0001 1.0001 0 1 0 19.707031 18.292969 L 13.414062 12 L 19.707031 5.7070312 A 1.0001 1.0001 0 0 0 18.980469 3.9902344 A 1.0001 1.0001 0 0 0 18.292969 4.2929688 L 12 10.585938 L 5.7070312 4.2929688 A 1.0001 1.0001 0 0 0 4.9902344 3.9902344 z'%3E%3C/path%3E%3C/svg%3E"); } -.modal-content { - border: 0; - box-shadow: 0 0.125rem 0.25rem rgb(0 0 0 / 8%) !important; +input.search-bar { + padding: 9px 9px 9px 40px; + background-color: white !important; + background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' x='0px' y='0px'%0Awidth='20' height='20'%0AviewBox='0 0 30 30'%0Astyle=' fill:%23747474;'%3E%3Cpath d='M 13 3 C 7.4889971 3 3 7.4889971 3 13 C 3 18.511003 7.4889971 23 13 23 C 15.396508 23 17.597385 22.148986 19.322266 20.736328 L 25.292969 26.707031 A 1.0001 1.0001 0 1 0 26.707031 25.292969 L 20.736328 19.322266 C 22.148986 17.597385 23 15.396508 23 13 C 23 7.4889971 18.511003 3 13 3 z M 13 5 C 17.430123 5 21 8.5698774 21 13 C 21 17.430123 17.430123 21 13 21 C 8.5698774 21 5 17.430123 5 13 C 5 8.5698774 8.5698774 5 13 5 z'%3E%3C/path%3E%3C/svg%3E") no-repeat 13px center; } + + diff --git a/app/javascript/components/shared/SearchBar.jsx b/app/javascript/components/shared/SearchBar.jsx index 688882bc53..8c8b1db9fb 100644 --- a/app/javascript/components/shared/SearchBar.jsx +++ b/app/javascript/components/shared/SearchBar.jsx @@ -8,7 +8,7 @@ export default function SearchBar({ id, setSearch }) { ( From 1941af8a3323cb507d44b95eb973e20cf9d954d4 Mon Sep 17 00:00:00 2001 From: scouillard Date: Wed, 11 May 2022 17:21:58 -0400 Subject: [PATCH 16/23] Cleaning up --- .../DeleteSharedAccessForm.jsx | 4 ++-- .../{ => shared_access_forms}/SharedAccessForm.jsx | 10 +++++----- .../components/shared_access/SharedAccessEmpty.jsx | 2 +- .../components/shared_access/SharedAccessList.jsx | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) rename app/javascript/components/forms/{ => shared_access_forms}/DeleteSharedAccessForm.jsx (85%) rename app/javascript/components/forms/{ => shared_access_forms}/SharedAccessForm.jsx (88%) diff --git a/app/javascript/components/forms/DeleteSharedAccessForm.jsx b/app/javascript/components/forms/shared_access_forms/DeleteSharedAccessForm.jsx similarity index 85% rename from app/javascript/components/forms/DeleteSharedAccessForm.jsx rename to app/javascript/components/forms/shared_access_forms/DeleteSharedAccessForm.jsx index cd46e45cc8..e9ab24a327 100644 --- a/app/javascript/components/forms/DeleteSharedAccessForm.jsx +++ b/app/javascript/components/forms/shared_access_forms/DeleteSharedAccessForm.jsx @@ -6,8 +6,8 @@ import { Button, Form } from 'react-bootstrap'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faTrashAlt } from '@fortawesome/free-regular-svg-icons'; import PropTypes from 'prop-types'; -import useDeleteSharedAccess from '../../hooks/mutations/shared_accesses/useDeleteSharedAccess'; -import RoomContext from '../../contexts/roomContext'; +import useDeleteSharedAccess from '../../../hooks/mutations/shared_accesses/useDeleteSharedAccess'; +import RoomContext from '../../../contexts/roomContext'; export default function DeleteSharedAccessForm({ userId }) { const { register, handleSubmit } = useForm(); diff --git a/app/javascript/components/forms/SharedAccessForm.jsx b/app/javascript/components/forms/shared_access_forms/SharedAccessForm.jsx similarity index 88% rename from app/javascript/components/forms/SharedAccessForm.jsx rename to app/javascript/components/forms/shared_access_forms/SharedAccessForm.jsx index dedb433148..4e1d02418c 100644 --- a/app/javascript/components/forms/SharedAccessForm.jsx +++ b/app/javascript/components/forms/shared_access_forms/SharedAccessForm.jsx @@ -6,11 +6,11 @@ import { } from 'react-bootstrap'; import { useForm } from 'react-hook-form'; import PropTypes from 'prop-types'; -import useShareAccess from '../../hooks/mutations/shared_accesses/useShareAccess'; -import Avatar from '../users/Avatar'; -import SearchBar from '../shared/SearchBar'; -import useShareableUsers from '../../hooks/queries/shared_accesses/useShareableUsers'; -import RoomContext from '../../contexts/roomContext'; +import useShareAccess from '../../../hooks/mutations/shared_accesses/useShareAccess'; +import Avatar from '../../users/Avatar'; +import SearchBar from '../../shared/SearchBar'; +import useShareableUsers from '../../../hooks/queries/shared_accesses/useShareableUsers'; +import RoomContext from '../../../contexts/roomContext'; export default function SharedAccessForm({ handleClose }) { const { register, handleSubmit } = useForm(); diff --git a/app/javascript/components/shared_access/SharedAccessEmpty.jsx b/app/javascript/components/shared_access/SharedAccessEmpty.jsx index 4052066252..fd9a709c32 100644 --- a/app/javascript/components/shared_access/SharedAccessEmpty.jsx +++ b/app/javascript/components/shared_access/SharedAccessEmpty.jsx @@ -3,7 +3,7 @@ import { Button, Card } from 'react-bootstrap'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faUser } from '@fortawesome/free-regular-svg-icons'; import Modal from '../shared/Modal'; -import SharedAccessForm from '../forms/SharedAccessForm'; +import SharedAccessForm from '../forms/shared_access_forms/SharedAccessForm'; export default function SharedAccessEmpty() { return ( diff --git a/app/javascript/components/shared_access/SharedAccessList.jsx b/app/javascript/components/shared_access/SharedAccessList.jsx index 9c5ef34b70..f0f5fe7bb5 100644 --- a/app/javascript/components/shared_access/SharedAccessList.jsx +++ b/app/javascript/components/shared_access/SharedAccessList.jsx @@ -4,8 +4,8 @@ import { } from 'react-bootstrap'; import PropTypes from 'prop-types'; import Modal from '../shared/Modal'; -import SharedAccessForm from '../forms/SharedAccessForm'; -import DeleteSharedAccessForm from '../forms/DeleteSharedAccessForm'; +import SharedAccessForm from '../forms/shared_access_forms/SharedAccessForm'; +import DeleteSharedAccessForm from '../forms/shared_access_forms/DeleteSharedAccessForm'; import Avatar from '../users/Avatar'; import SearchBar from '../shared/SearchBar'; From 872a91b750a54f9df7c93d3b0779f777b6d86363 Mon Sep 17 00:00:00 2001 From: scouillard Date: Wed, 11 May 2022 17:36:27 -0400 Subject: [PATCH 17/23] Rubocop --- app/assets/stylesheets/application.bootstrap.scss | 3 +-- app/models/shared_access.rb | 1 - db/migrate/20220505192018_create_shared_accesses.rb | 2 ++ db/seeds.rb | 5 ++--- spec/controllers/shared_accesses_controller_spec.rb | 4 ++-- test/models/shared_access_test.rb | 4 +++- 6 files changed, 10 insertions(+), 9 deletions(-) diff --git a/app/assets/stylesheets/application.bootstrap.scss b/app/assets/stylesheets/application.bootstrap.scss index 78dc667ee2..a97eb0bf4c 100644 --- a/app/assets/stylesheets/application.bootstrap.scss +++ b/app/assets/stylesheets/application.bootstrap.scss @@ -61,8 +61,7 @@ input[type='search']::-webkit-search-cancel-button { input.search-bar { padding: 9px 9px 9px 40px; - background-color: white !important; - background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' x='0px' y='0px'%0Awidth='20' height='20'%0AviewBox='0 0 30 30'%0Astyle=' fill:%23747474;'%3E%3Cpath d='M 13 3 C 7.4889971 3 3 7.4889971 3 13 C 3 18.511003 7.4889971 23 13 23 C 15.396508 23 17.597385 22.148986 19.322266 20.736328 L 25.292969 26.707031 A 1.0001 1.0001 0 1 0 26.707031 25.292969 L 20.736328 19.322266 C 22.148986 17.597385 23 15.396508 23 13 C 23 7.4889971 18.511003 3 13 3 z M 13 5 C 17.430123 5 21 8.5698774 21 13 C 21 17.430123 17.430123 21 13 21 C 8.5698774 21 5 17.430123 5 13 C 5 8.5698774 8.5698774 5 13 5 z'%3E%3C/path%3E%3C/svg%3E") no-repeat 13px center; + background: white url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' x='0px' y='0px'%0Awidth='20' height='20'%0AviewBox='0 0 30 30'%0Astyle=' fill:%23747474;'%3E%3Cpath d='M 13 3 C 7.4889971 3 3 7.4889971 3 13 C 3 18.511003 7.4889971 23 13 23 C 15.396508 23 17.597385 22.148986 19.322266 20.736328 L 25.292969 26.707031 A 1.0001 1.0001 0 1 0 26.707031 25.292969 L 20.736328 19.322266 C 22.148986 17.597385 23 15.396508 23 13 C 23 7.4889971 18.511003 3 13 3 z M 13 5 C 17.430123 5 21 8.5698774 21 13 C 21 17.430123 17.430123 21 13 21 C 8.5698774 21 5 17.430123 5 13 C 5 8.5698774 8.5698774 5 13 5 z'%3E%3C/path%3E%3C/svg%3E") no-repeat 13px center; } diff --git a/app/models/shared_access.rb b/app/models/shared_access.rb index 28584668eb..64ac116128 100644 --- a/app/models/shared_access.rb +++ b/app/models/shared_access.rb @@ -6,4 +6,3 @@ class SharedAccess < ApplicationRecord validates :shared_user, uniqueness: { scope: :shared_room } end - diff --git a/db/migrate/20220505192018_create_shared_accesses.rb b/db/migrate/20220505192018_create_shared_accesses.rb index 2b718be6e6..17266ff082 100644 --- a/db/migrate/20220505192018_create_shared_accesses.rb +++ b/db/migrate/20220505192018_create_shared_accesses.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CreateSharedAccesses < ActiveRecord::Migration[7.0] def change create_table :shared_accesses do |t| diff --git a/db/seeds.rb b/db/seeds.rb index 687c98c0fc..a4ade86713 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + # This file should contain all the record creation needed to seed the database with its default values. # The data can then be loaded with the bin/rails db:seed command (or created alongside the database with db:setup). # @@ -14,8 +15,6 @@ name: Faker::Name.name, password: '12345678', password_confirmation: '12345678', - provider: 'greenlight', + provider: 'greenlight' ) end - - diff --git a/spec/controllers/shared_accesses_controller_spec.rb b/spec/controllers/shared_accesses_controller_spec.rb index 258956756f..6fcdf04059 100644 --- a/spec/controllers/shared_accesses_controller_spec.rb +++ b/spec/controllers/shared_accesses_controller_spec.rb @@ -36,7 +36,7 @@ room = create(:room) user = create(:user) create(:shared_access, user_id: user.id, room_id: room.id) - delete :destroy, params: { room_id: room.id, user_id: user.id} + delete :destroy, params: { room_id: room.id, user_id: user.id } expect(user.shared_rooms).not_to include(room) end @@ -46,7 +46,7 @@ random_user = create(:user) create(:shared_access, user_id: user.id, room_id: room.id) create(:shared_access, user_id: random_user.id, room_id: room.id) - delete :destroy, params: { room_id: room.id, user_id: random_user.id} + delete :destroy, params: { room_id: room.id, user_id: random_user.id } expect(user.shared_rooms).to include(room) end end diff --git a/test/models/shared_access_test.rb b/test/models/shared_access_test.rb index 53af10f41d..0aea0e2e8b 100644 --- a/test/models/shared_access_test.rb +++ b/test/models/shared_access_test.rb @@ -1,4 +1,6 @@ -require "test_helper" +# frozen_string_literal: true + +require 'test_helper' class SharedAccessTest < ActiveSupport::TestCase # test "the truth" do From e82f1fdc6ca32d167582a07e228a32a130a75018 Mon Sep 17 00:00:00 2001 From: scouillard Date: Thu, 12 May 2022 14:29:33 -0400 Subject: [PATCH 18/23] bla --- .github/workflows/{ci.yml => workflow.yml} | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) rename .github/workflows/{ci.yml => workflow.yml} (97%) diff --git a/.github/workflows/ci.yml b/.github/workflows/workflow.yml similarity index 97% rename from .github/workflows/ci.yml rename to .github/workflows/workflow.yml index b94690f15a..1ace64ed9d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/workflow.yml @@ -3,11 +3,13 @@ env: DATABASE_URL: postgres://postgres:postgres@localhost/greenlight-actions RAILS_ENV: test -name: CI +name: CI-test on: + workflow_dispatch: pull_request: branches: "*" + jobs: test: name: Rubocop + RSpec + ESLint + Jest From 34cc8cad34223d8f7441ee14711125f5c7483d51 Mon Sep 17 00:00:00 2001 From: scouillard Date: Thu, 12 May 2022 15:07:44 -0400 Subject: [PATCH 19/23] Debugging RSPec --- .github/workflows/workflow.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 1ace64ed9d..ee5e408b1d 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -3,9 +3,8 @@ env: DATABASE_URL: postgres://postgres:postgres@localhost/greenlight-actions RAILS_ENV: test -name: CI-test +name: CI on: - workflow_dispatch: pull_request: branches: "*" @@ -60,11 +59,13 @@ jobs: bundler exec rails db:create bundler exec rails db:migrate - - name: Run Rubocop - run: bundle exec rubocop --parallel +# - name: Run Rubocop +# run: bundle exec rubocop --parallel - name: Run RSpec - run: bundle exec rspec + run: | + tail -f log/test.log & + bundle exec rspec - name: Run ESLint run: npx eslint app/javascript/* --ext .js,.jsx From a5ce8c31bd2fdd7c0263f496067e82cae1a0c795 Mon Sep 17 00:00:00 2001 From: scouillard Date: Thu, 12 May 2022 15:29:07 -0400 Subject: [PATCH 20/23] add debug puts --- spec/controllers/shared_accesses_controller_spec.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/controllers/shared_accesses_controller_spec.rb b/spec/controllers/shared_accesses_controller_spec.rb index 6fcdf04059..12e76dfab9 100644 --- a/spec/controllers/shared_accesses_controller_spec.rb +++ b/spec/controllers/shared_accesses_controller_spec.rb @@ -53,6 +53,7 @@ describe '#shared_users' do it 'lists the users that the room has been shared to' do + puts "\n\n\n\n HERE IN SHARED USERS \n\n\n\n" room = create(:room) users = create_list(:user, 10) shared_users = [] @@ -69,6 +70,7 @@ end describe '#shareable_users' do + puts "\n\n\n\n HERE IN SHAREABLE USERS \n\n\n\n" it 'lists the users that the room can be shared to' do room = create(:room) users = create_list(:user, 10) From 8ba3d0e7205fbcf684a48108fd52502e19950594 Mon Sep 17 00:00:00 2001 From: scouillard Date: Thu, 12 May 2022 15:44:32 -0400 Subject: [PATCH 21/23] debug rspec --- spec/controllers/shared_accesses_controller_spec.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/controllers/shared_accesses_controller_spec.rb b/spec/controllers/shared_accesses_controller_spec.rb index 12e76dfab9..10b34931f0 100644 --- a/spec/controllers/shared_accesses_controller_spec.rb +++ b/spec/controllers/shared_accesses_controller_spec.rb @@ -66,12 +66,13 @@ get :shared_users, params: { room_id: room.id } shared_user_response = JSON.parse(response.body)['data'].map { |user| user['id'] } expect(shared_user_response).to eql(shared_users.pluck(:id)) + puts "\n\n\n\n FINISH SHARE USERS \n\n\n\n" end end describe '#shareable_users' do - puts "\n\n\n\n HERE IN SHAREABLE USERS \n\n\n\n" it 'lists the users that the room can be shared to' do + puts "\n\n\n\n HERE IN SHAREABLE USERS \n\n\n\n" room = create(:room) users = create_list(:user, 10) shareable_users = [] @@ -87,6 +88,7 @@ get :shareable_users, params: { room_id: room.id } shareable_user_response = JSON.parse(response.body)['data'].map { |user| user['id'] } expect(shareable_user_response).to eql(shareable_users.pluck(:id)) + puts "\n\n\n\n FINISH SHAREABLE USERS \n\n\n\n" end end end From 5f0865350a11232953a4a85219a7a6f733aeca84 Mon Sep 17 00:00:00 2001 From: scouillard Date: Thu, 12 May 2022 16:09:25 -0400 Subject: [PATCH 22/23] debug link_tree --- app/assets/images/.keep | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 app/assets/images/.keep diff --git a/app/assets/images/.keep b/app/assets/images/.keep deleted file mode 100644 index e69de29bb2..0000000000 From d67940c374655262e7a2b85edb18b2401e2cc3f9 Mon Sep 17 00:00:00 2001 From: scouillard Date: Thu, 12 May 2022 16:15:24 -0400 Subject: [PATCH 23/23] debug link_tree --- app/assets/builds/.keep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 app/assets/builds/.keep diff --git a/app/assets/builds/.keep b/app/assets/builds/.keep new file mode 100644 index 0000000000..e69de29bb2