diff --git a/Gemfile b/Gemfile
index fd0e2e8c2..1af9ee447 100644
--- a/Gemfile
+++ b/Gemfile
@@ -108,7 +108,6 @@ gem "store_attribute", "~> 0.8.1" # ActiveRecord extension which adds typecastin
gem "storext", "~> 3.3" # Add type-casting and other features on top of ActiveRecord::Store.store_accessor
gem "stripe", "~> 5.30" # Ruby library for the Stripe API
gem "strong_migrations", "~> 0.7" # Catch unsafe migrations
-gem "twilio-ruby", "~> 5.48" # The official library for communicating with the Twilio REST API
gem "twitter", "~> 7.0" # A Ruby interface to the Twitter API
gem "uglifier", "~> 4.2" # Uglifier minifies JavaScript files
gem "ulid", "~> 1.3" # Universally Unique Lexicographically Sortable Identifier implementation for Ruby
diff --git a/Gemfile.lock b/Gemfile.lock
index 2251eab6f..94e875db9 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -808,10 +808,6 @@ GEM
thread_safe (0.3.6)
tilt (2.0.10)
timecop (0.9.4)
- twilio-ruby (5.48.0)
- faraday (>= 0.9, < 2.0)
- jwt (>= 1.5, <= 2.5)
- nokogiri (>= 1.6, < 2.0)
twitter (7.0.0)
addressable (~> 2.3)
buftok (~> 0.2.0)
@@ -1028,7 +1024,6 @@ DEPENDENCIES
strong_migrations (~> 0.7)
test-prof (~> 1.0)
timecop (~> 0.9)
- twilio-ruby (~> 5.48)
twitter (~> 7.0)
uglifier (~> 4.2)
ulid (~> 1.3)
diff --git a/app/controllers/twilio_tokens_controller.rb b/app/controllers/twilio_tokens_controller.rb
deleted file mode 100644
index ae6be1e2a..000000000
--- a/app/controllers/twilio_tokens_controller.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-class TwilioTokensController < ApplicationController
- after_action :verify_authorized
-
- def show
- video_channel = params[:id].split("private-video-channel-")[1] if params[:id].start_with?("private-video-channel-")
- unless video_channel
- skip_authorization
- render json: { status: "failure", token: @twilio_token }, status: :not_found
- return
- end
- @chat_channel = ChatChannel.find(video_channel.to_i)
- authorize @chat_channel # show pundit method for chat_channel_policy works here, should always check though
- @twilio_token = Twilio::GetJwtToken.call(current_user, params[:id])
- render json: { status: "success", token: @twilio_token }, status: :ok
- end
-end
diff --git a/app/controllers/video_chats_controller.rb b/app/controllers/video_chats_controller.rb
deleted file mode 100644
index fbc5eac27..000000000
--- a/app/controllers/video_chats_controller.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-class VideoChatsController < ApplicationController
- before_action :authenticate_user!
- after_action :verify_authorized
-
- def show
- @chat_channel = ChatChannel.find(params[:id]) || not_found
- authorize @chat_channel
-
- account_sid = ApplicationConfig["TWILIO_ACCOUNT_SID"]
- api_key = ApplicationConfig["TWILIO_VIDEO_API_KEY"]
- api_secret = ApplicationConfig["TWILIO_VIDEO_API_SECRET"]
- @username = display_username
- @video_type = video_type
- token = Twilio::JWT::AccessToken.new(
- account_sid,
- api_key,
- api_secret,
- [],
- identity: @username,
- )
-
- grant = Twilio::JWT::AccessToken::VideoGrant.new
- grant.room = params[:id]
- token.add_grant(grant)
-
- @token = token.to_jwt
- end
-
- private
-
- def display_username
- return "@#{params[:username]}" if params[:username] && Rails.env.development? # simpler solo testing in dev
-
- "@#{current_user.username}"
- end
-
- def video_type
- if @chat_channel.channel_type == "direct" || @chat_channel.chat_channel_memberships.size < 5
- "peer-to-peer"
- else
- "group"
- end
- end
-end
diff --git a/app/javascript/chat/__tests__/VideoContent.test.jsx b/app/javascript/chat/__tests__/VideoContent.test.jsx
deleted file mode 100644
index 852499038..000000000
--- a/app/javascript/chat/__tests__/VideoContent.test.jsx
+++ /dev/null
@@ -1,77 +0,0 @@
-import { h } from 'preact';
-import { axe } from 'jest-axe';
-import { render } from '@testing-library/preact';
-import { VideoContent } from '../videoContent';
-
-describe('', () => {
- it('should have no a11y violations', async () => {
- const { container } = render(
- ,
- );
- const results = await axe(container);
-
- expect(results).toHaveNoViolations();
- });
-
- it('should render in fullscreen', () => {
- const { queryByLabelText } = render(
- ,
- );
-
- expect(queryByLabelText('Leave fullscreen')).toBeNull();
- expect(queryByLabelText('Fullscreen')).toBeDefined();
- });
-
- it('should not render in fullscreen', () => {
- const { queryByLabelText } = render(
- ,
- );
-
- expect(queryByLabelText('Fullscreen')).toBeNull();
- expect(queryByLabelText('Leave fullscreen')).toBeDefined();
- });
-
- it('should trigger video content when clicked', () => {
- const onTriggerVideoContent = jest.fn();
-
- const { getByTestId } = render(
- ,
- );
-
- getByTestId('connect-video').click();
-
- expect(onTriggerVideoContent).toHaveBeenCalledTimes(1);
- });
-
- it('should load the given video', () => {
- const onTriggerVideoContent = jest.fn();
-
- const { getByTitle } = render(
- ,
- );
-
- const videoFrame = getByTitle('Video display');
-
- expect(videoFrame.getAttribute('src')).toEqual('/some-video-path');
- });
-});
diff --git a/app/javascript/chat/chat.jsx b/app/javascript/chat/chat.jsx
index 9a2c3228f..a083a1879 100644
--- a/app/javascript/chat/chat.jsx
+++ b/app/javascript/chat/chat.jsx
@@ -36,7 +36,6 @@ import { Compose } from './compose';
import { Message } from './message';
import { ActionMessage } from './actionMessage';
import { Content } from './content';
-import { VideoContent } from './videoContent';
import { DragAndDropZone } from '@utilities/dragAndDrop';
import { dragAndUpload } from '@utilities/dragAndUpload';
import { Button } from '@crayons';
@@ -83,7 +82,6 @@ export class Chat extends Component {
notificationsPermission: null,
activeContent: {},
fullscreenContent: null,
- videoPath: null,
expanded: window.innerWidth > NARROW_WIDTH_LIMIT,
isMobileDevice: typeof window.orientation !== 'undefined',
subscribedPusherChannels: [],
@@ -715,14 +713,6 @@ export class Chat extends Component {
// should check if user has the privilege
if (message.startsWith('/code')) {
this.setActiveContentState(activeChannelId, { type_of: 'code_editor' });
- } else if (message.startsWith('/call')) {
- const messageObject = {
- activeChannelId,
- message: '/call',
- mentionedUsersId: this.getMentionedUsers(message),
- };
- this.setState({ videoPath: `/video_chats/${activeChannelId}` });
- sendMessage(messageObject, this.handleSuccess, this.handleFailure);
} else if (message.startsWith('/play ')) {
const messageObject = {
activeChannelId,
@@ -1008,17 +998,6 @@ export class Chat extends Component {
path: `/chat_channel_memberships/${activeChannel.id}/edit`,
type_of: 'article',
});
- } else if (content.startsWith('sidecar-content-plus-video')) {
- this.setActiveContentState(activeChannelId, {
- type_of: 'loading-post',
- });
- this.setActiveContent({
- path: target.href || target.parentElement.href,
- type_of: 'article',
- });
- this.setState({ videoPath: `/video_chats/${activeChannelId}` });
- } else if (content.startsWith('sidecar-video')) {
- this.setState({ videoPath: target.href || target.parentElement.href });
} else if (
content.startsWith('sidecar') ||
content.startsWith('article')
@@ -1596,11 +1575,6 @@ export class Chat extends Component {
fullscreen={state.fullscreenContent === 'sidecar'}
closeReportAbuseForm={this.closeReportAbuseForm}
/>
-
);
};
@@ -1626,23 +1600,6 @@ export class Chat extends Component {
}
};
- onTriggerVideoContent = (e) => {
- if (e.target.dataset.content === 'exit') {
- this.setState({
- videoPath: null,
- fullscreenContent: null,
- expanded: window.innerWidth > 600,
- });
- } else if (this.state.fullscreenContent === 'video') {
- this.setState({ fullscreenContent: null });
- } else {
- this.setState({
- fullscreenContent: 'video',
- expanded: window.innerWidth > WIDE_WIDTH_LIMIT,
- });
- }
- };
-
handleMention = (e) => {
const { activeChannel } = this.state;
const mention = e.keyCode === 64;
@@ -2010,12 +1967,10 @@ export class Chat extends Component {
data-testid="chat"
className={`chat chat--expanded
chat--${
- state.videoPath ? 'video-visible' : 'video-not-visible'
- } chat--${
- state.activeContent[state.activeChannelId]
- ? 'content-visible'
- : 'content-not-visible'
- } ${fullscreenMode}`}
+ state.activeContent[state.activeChannelId]
+ ? 'content-visible'
+ : 'content-not-visible'
+ } ${fullscreenMode}`}
data-no-instant
aria-expanded={state.expanded}
>
diff --git a/app/javascript/chat/content.jsx b/app/javascript/chat/content.jsx
index 6359618d1..f69db256c 100644
--- a/app/javascript/chat/content.jsx
+++ b/app/javascript/chat/content.jsx
@@ -14,7 +14,7 @@ const smartSvgIcon = (content, d) => (
viewBox="0 0 24 24"
width="24"
height="24"
- style={{ marginLeft:'-12px', marginTop:'-4px' }}
+ style={{ marginLeft: '-12px', marginTop: '-4px' }}
>
@@ -75,7 +75,7 @@ export class Content extends Component {
type="button"
className="activechatchannel__activecontentexitbutton activechatchannel__activecontentexitbutton--fullscreen crayons-btn crayons-btn--secondary"
data-content="fullscreen"
- style={{ left: "-80px", marginLeft: "0px" }}
+ style={{ left: '-80px', marginLeft: '0px' }}
title="fullscreen"
>
{' '}
diff --git a/app/javascript/chat/videoContent.jsx b/app/javascript/chat/videoContent.jsx
deleted file mode 100644
index ea32464bc..000000000
--- a/app/javascript/chat/videoContent.jsx
+++ /dev/null
@@ -1,70 +0,0 @@
-import { h } from 'preact';
-
-const smartSvgIcon = (content, d) => (
-
-);
-
-/**
- * Displays video in Connect.
- *
- * @param {string} props.videoPath The URL of a video
- * @param {function} props.onTriggerVideoContent Fires when a video is clicked on
- * @param {boolean} props.fullscreen Whether or not to show the video in fullscreen
- *
- * @returns A video component
- */
-export function VideoContent(props) {
- const { videoPath, onTriggerVideoContent, fullscreen } = props;
-
- if (!videoPath) {
- return null;
- }
-
- return (
-
-
-
-
-
- );
-}
diff --git a/app/javascript/packs/videoChat.jsx b/app/javascript/packs/videoChat.jsx
deleted file mode 100644
index bee9449d6..000000000
--- a/app/javascript/packs/videoChat.jsx
+++ /dev/null
@@ -1,130 +0,0 @@
-const { createLocalVideoTrack, connect } = require('twilio-video');
-const root = document.getElementById('videochat');
-const remoteMedia = document.getElementById('remote-media');
-const muteButton = document.getElementById('mute-toggle');
-const videoToggleButton = document.getElementById('videohide-toggle');
-const localMediaContainer = document.getElementById('local-media');
-const roomType = document.getElementById('room-type').dataset.type; //Group
-
-connect(root.dataset.token, {
- name: 'room-name',
- audio: true,
- type: roomType,
- video: { width: 800 },
-}).then((room) => {
- room.participants.forEach(participantConnected);
- room.on('participantConnected', participantConnected);
-
- room.on('participantDisconnected', participantDisconnected);
- room.once('disconnected', (_error) =>
- room.participants.forEach(participantDisconnected),
- );
- muteButton.onclick = function (e) {
- e.preventDefault();
- room.localParticipant.audioTracks.forEach(function (trackPub) {
- if (muteButton.dataset.muted === 'true') {
- muteButton.dataset.muted = 'false';
- muteButton.classList.remove('crayons-btn--danger');
- muteButton.innerHTML = 'Mute';
- trackPub.track.enable();
- } else {
- muteButton.dataset.muted = 'true';
- muteButton.classList.add('crayons-btn--danger');
- muteButton.innerHTML = 'Unmute';
- trackPub.track.disable();
- }
- });
- };
-
- videoToggleButton.onclick = function (e) {
- e.preventDefault();
- room.localParticipant.videoTracks.forEach(function (trackPub) {
- if (videoToggleButton.dataset.hidden === 'true') {
- videoToggleButton.dataset.hidden = 'false';
- videoToggleButton.classList.remove('crayons-btn--danger');
- videoToggleButton.innerHTML = 'Hide';
- localMediaContainer.classList.remove('video-hidden');
- trackPub.track.enable();
- } else {
- videoToggleButton.dataset.hidden = 'true';
- videoToggleButton.classList.add('crayons-btn--danger');
- videoToggleButton.innerHTML = 'Unhide';
- localMediaContainer.classList.add('video-hidden');
- trackPub.track.disable();
- }
- });
- };
-});
-
-createLocalVideoTrack().then((track) => {
- localMediaContainer.appendChild(track.attach());
- document.getElementById('video-controls').classList.add('showing');
-});
-
-function participantConnected(participant) {
- const numExistingDivs = document.getElementsByClassName('individual-video')
- .length;
- const div = document.createElement('div');
- div.id = participant.sid;
- div.className = `individual-video${
- numExistingDivs > 3 ? ' one-of-many-videos' : ''
- }`;
- div.innerHTML = `\
-
${participant.identity}
\
-
audio off
\
-
video off
\
-
`;
-
- participant.on('trackSubscribed', (track) => trackSubscribed(div, track));
- participant.on('trackUnsubscribed', trackUnsubscribed);
-
- participant.on('trackDisabled', (track) => trackDisabled(div, track));
- participant.on('trackEnabled', (track) => trackEnabled(div, track));
-
- participant.tracks.forEach((publication) => {
- if (publication.isSubscribed) {
- trackSubscribed(div, publication.track);
- }
- });
- remoteMedia.appendChild(div);
-}
-
-function participantDisconnected(participant) {
- document.getElementById(participant.sid).remove();
-}
-
-function trackSubscribed(div, track) {
- div.appendChild(track.attach());
- if (!track.isEnabled) {
- if (track.kind === 'video') {
- div.classList.add('disabled-video');
- } else {
- div.classList.add('disabled-audio');
- }
- }
-}
-
-function trackUnsubscribed(track) {
- track.detach().forEach((element) => element.remove());
-}
-
-function trackDisabled(div, track) {
- if (track.kind === 'video') {
- div.classList.add('disabled-video');
- } else {
- div.classList.add('disabled-audio');
- }
-}
-
-function trackEnabled(div, track) {
- if (track.kind === 'video') {
- div.classList.remove('disabled-video');
- } else {
- div.classList.remove('disabled-audio');
- }
-}
-
-if (navigator.userAgent === 'DEV-Native-ios') {
- root.innerHTML =
- '';
-}
diff --git a/app/models/message.rb b/app/models/message.rb
index 3631908b1..7725cc770 100644
--- a/app/models/message.rb
+++ b/app/models/message.rb
@@ -158,14 +158,6 @@ class Message < ApplicationRecord
# rubocop:disable Rails/OutputSafety
def handle_slash_command(html)
response = case html.to_s.strip
- when "/call
"
- "
-
- Let's video chat 😄
-
- ".html_safe
when "/play codenames
" # proof of concept
"
- body {
- height: 100vh;
- overflow: hidden;
- }
- .video-chat-wrapper {
- position: relative;
- height: 100vh;
- overflow: scroll;
- }
- .individual-video video {
- max-height: 100vh;
- background: #232324;
- width: 100%;
- }
-
- #remote-media {
- display:flex;
- flex-wrap: wrap;
- /* flex-direction:row; */
- }
-
- .individual-video {
- overflow: hidden;
- position: relative;
- margin-bottom: -6px;
- flex: 1 0 21%;
- text-align: center;
- min-width:20%;
- background: #232324;
- }
- .one-of-many-videos {
- min-width:33%;
- }
- /* .individual-video:last-child {
- margin-bottom: 100px;
- } */
-
- #local-media video {
- width: 150px;
- position: fixed;
- bottom: 10px;
- right: 10px;
- z-index: 5;
- border-radius:3px;
- border: 1px solid #232324;
- }
-
- #local-media.video-hidden {
- opacity: 0.1;
- }
-
- .participant-info {
- padding: 3px 8px;
- border-radius: 3px;
- position: absolute;
- bottom: 0;
- left: 0;
- right: 0;
- font-size: 13px;
- text-align: center;
- }
- .participant-name {
- display: inline-block;
- color: white;
- background: #171717;
- padding: 4px 12px;
- border-radius: 100px;
- margin: 8px;
- }
- .video-controls {
- position: fixed;
- bottom: 15px;
- left: 5px;
- width: calc(100% - 175px);
- z-index: 10;
- display: none;
- }
-
- .showing {
- display: block;
- }
-
- .video-controls button {
- font-size: 15px;
- width: 100%;
- margin: 3px 0px;
- padding: 3px 0px;
- }
-
- .disabled-video video {
- opacity: 0.1;
- }
- .disabled-audio-indicator,.disabled-video-indicator {
- background: red;
- color: white;
- display: none;
- position: absolute;
- border-radius: 100px;
- padding: 4px 10px;
- right: 5px;
- bottom: 12px;
- }
- .disabled-audio-indicator {
- right: auto;
- left: 5px;
- }
- .disabled-video .disabled-video-indicator{
- display: inline-block;
- }
- .disabled-audio .disabled-audio-indicator{
- display: inline-block;
- }
- .platform-unavailable-message {
- padding: 20px;
- color: white;
- margin-top: 30px;
- }
-
- @media screen and (max-width: 400px) {
- .individual-video {
- flex: 1 0 100%;
- width: 100%;
- }
- }
- @media screen and (max-width: 280px) {
- .video-controls {
- width: 92%;
- bottom: 5px;
- }
- #local-media video {
- width: 90%;
- bottom: 85px;
- }
- }
-
- @media screen and (min-width: 500px) {
- .video-controls {
- left: auto;
- right: 170px;
- width: 200px;
- }
- }
-
- @media screen and (max-height: 400px) {
- .video-controls {
- width: 90px;
- }
- #local-media video {
- width: 90px;
- bottom: 5px;
- }
- }
-
-
-
-
-
-
-
-
-
-
-
-<%# TODO Make paramsp[:id] map to a chat channel and ensure the users have permission %>
-<%# That part is pretty straightforward %>
-<%# Then close the loop on the JavaScript part of it. %>
-<%# Also should be *fairly* straightforward in terms of following a basic tutorial. %>
-<%# Then we drop a link in chat rather than trying to make video work with complicated state in chat. %>
-<%# The complicated part has always been chat state/channels etc. Doing in sidecar means more containerization of logic. %>
-<%= javascript_packs_with_chunks_tag "videoChat", defer: true %>
diff --git a/config/routes.rb b/config/routes.rb
index 2d8718baf..006c86ec2 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -95,7 +95,8 @@ Rails.application.routes.draw do
get "/badges/badge_achievements", to: redirect("/admin/badge_achievements")
get "/badges/badge_achievements/award_badges", to: redirect("/admin/badge_achievements/award_badges")
- # NOTE: @ridhwana These routes below will be deleted once we remove the admin_restructure feature flag, hence they've been regrouped them in this manner.
+ # NOTE: @ridhwana These routes below will be deleted once we remove the
+ # admin_restructure feature flag, hence they've been regrouped in this manner.
resources :articles, only: %i[index show update]
resources :badges, only: %i[index edit update new create]
resources :badge_achievements, only: %i[index destroy]
diff --git a/package.json b/package.json
index dfab5ec85..839b3eb37 100644
--- a/package.json
+++ b/package.json
@@ -177,7 +177,6 @@
"pusher-js": "^7.0.3",
"rails-erb-loader": "^5.5.2",
"stimulus": "^2.0.0",
- "twilio-video": "^2.13.0",
"web-share-wrapper": "^0.2.1"
},
"resolutions": {
diff --git a/spec/models/message_spec.rb b/spec/models/message_spec.rb
index 21ae1ff93..c58d3c807 100644
--- a/spec/models/message_spec.rb
+++ b/spec/models/message_spec.rb
@@ -68,13 +68,6 @@ RSpec.describe Message, type: :model do
expect(message.message_html).to include("sidecar-user")
end
- it "creates rich call link" do
- message.message_markdown = "/call"
- message.validate!
-
- expect(message.message_html).to include("sidecar-video")
- end
-
it "creates rich embeddable link" do
message.message_markdown = "https://docs.google.com/ https://www.figma.com/file/"
message.validate!
diff --git a/spec/requests/twilio_tokens_spec.rb b/spec/requests/twilio_tokens_spec.rb
deleted file mode 100644
index a64b70a7f..000000000
--- a/spec/requests/twilio_tokens_spec.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-require "rails_helper"
-
-RSpec.describe "TwilioTokens", type: :request do
- let(:user) { create(:user) }
- let(:chat_channel) { create(:chat_channel) }
-
- before do
- sign_in user
- end
-
- describe "GET /twilio_tokens/:id" do
- it "returns a token for member of a channel" do
- chat_channel.add_users [user]
- get "/twilio_tokens/private-video-channel-#{chat_channel.id}"
- expect(response.status).to eq(200)
- end
-
- it "returns not found if unknown ID" do
- chat_channel.add_users [user]
- get "/twilio_tokens/sddds3423443efrwdfsd"
- expect(response.status).to eq(404)
- end
-
- it "returns not found if wrong ID prefix" do
- chat_channel.add_users [user]
- get "/twilio_tokens/sddds3423443efrwdfsd-#{chat_channel.id}"
- expect(response.status).to eq(404)
- end
-
- # it "returns unauthorized if user not member of channel" do
- # expect { get "/twilio_tokens/private-video-channel-#{chat_channel.id}" }.
- # to raise_error(Pundit::NotAuthorizedError)
- # end
- end
-end
diff --git a/spec/requests/video_chats_spec.rb b/spec/requests/video_chats_spec.rb
deleted file mode 100644
index 240768d01..000000000
--- a/spec/requests/video_chats_spec.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-require "rails_helper"
-
-RSpec.describe "VideoChats", type: :request do
- let(:user) { create(:user) }
- let(:second_user) { create(:user) }
- let(:third_user) { create(:user) }
-
- describe "GET /video_chats/:id" do
- context "with user signed in" do
- before do
- sign_in user
- end
-
- it "displays basic html for working" do
- channel = ChatChannels::CreateWithUsers.call(users: [user, second_user])
- get "/video_chats/#{channel.id}"
- expect(response.body).to include("