diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz
index bc8ddb5..db20435 100644
Binary files a/.yarn/install-state.gz and b/.yarn/install-state.gz differ
diff --git a/Gemfile b/Gemfile
index 84a5158..ef4975e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -75,9 +75,6 @@ gem "postmark-rails"
# Subscription management
gem "stripe"
-# Chat
-gem "stream-chat-ruby"
-
# Markdown support
gem "redcarpet"
diff --git a/Gemfile.lock b/Gemfile.lock
index b76e605..32bff38 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -85,7 +85,6 @@ GEM
regexp_parser (>= 1.5, < 3.0)
xpath (~> 3.2)
concurrent-ruby (1.2.2)
- connection_pool (2.4.1)
content_disposition (1.0.0)
crass (1.0.6)
cssbundling-rails (1.3.3)
@@ -119,9 +118,6 @@ GEM
faraday-multipart (1.0.4)
multipart-post (~> 2)
faraday-net_http (3.0.2)
- faraday-net_http_persistent (2.1.0)
- faraday (~> 2.5)
- net-http-persistent (~> 4.0)
ffi (1.16.1)
globalid (1.2.1)
activesupport (>= 6.1)
@@ -193,8 +189,6 @@ GEM
msgpack (1.7.2)
multi_json (1.15.0)
multipart-post (2.3.0)
- net-http-persistent (4.0.2)
- connection_pool (~> 2.2)
net-imap (0.3.7)
date
net-protocol
@@ -316,7 +310,6 @@ GEM
faraday (>= 0.17.5, < 3.a)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
- sorbet-runtime (0.5.11074)
sprockets (4.2.1)
concurrent-ruby (~> 1.0)
rack (>= 2.2.4, < 4)
@@ -326,13 +319,6 @@ GEM
sprockets (>= 3.0.0)
stimulus-rails (1.2.2)
railties (>= 6.0.0)
- stream-chat-ruby (3.0.0)
- faraday
- faraday-multipart
- faraday-net_http_persistent
- jwt
- net-http-persistent
- sorbet-runtime
stringio (3.0.8)
stripe (9.4.0)
thor (1.2.2)
@@ -391,7 +377,6 @@ DEPENDENCIES
shrine-google_cloud_storage
sprockets-rails
stimulus-rails
- stream-chat-ruby
stripe
turbo-rails
tzinfo-data
diff --git a/app/assets/stylesheets/application.bootstrap.scss b/app/assets/stylesheets/application.bootstrap.scss
index 7bc1b3e..6bf2c8b 100644
--- a/app/assets/stylesheets/application.bootstrap.scss
+++ b/app/assets/stylesheets/application.bootstrap.scss
@@ -4,12 +4,48 @@ $primary: #262626;
@import 'bootstrap/scss/bootstrap';
@import 'bootstrap-icons/font/bootstrap-icons';
+@import 'channels';
@import 'chats';
@import 'home';
@import 'location';
+@import 'profiles';
@import 'search_location';
@import 'visas';
.navbar-actions {
display: flex;
}
+
+.profile-picture {
+ border-radius: 50%;
+
+ height: 180px;
+ width: 180px;
+
+ &.chat-profile-picture {
+ margin-right: 4px;
+
+ height: 36px;
+ width: 36px;
+
+ &.placeholder {
+ font-size: 1rem;
+ }
+ }
+
+ &.placeholder {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ background-color: lighten($primary, 20%);
+ color: $white;
+
+ font-size: 5rem;
+
+ &:hover {
+ cursor: default;
+ }
+ }
+}
+
diff --git a/app/assets/stylesheets/channels.scss b/app/assets/stylesheets/channels.scss
new file mode 100644
index 0000000..d292cc2
--- /dev/null
+++ b/app/assets/stylesheets/channels.scss
@@ -0,0 +1,137 @@
+.chat-channel-section {
+ flex: 1;
+
+ display: flex;
+ flex-direction: column;
+
+ .channel-header {
+ flex: 0;
+
+ display: flex;
+ flex-direction: row;
+
+ border-bottom: 1px solid $primary;
+
+ .leave-channel-button {
+ color: $danger;
+ }
+ }
+
+ .channel-message-section {
+ flex: 1 0 0;
+ overflow: scroll;
+
+ display: flex;
+ flex-direction: column-reverse;
+
+ .chat-message {
+ display: flex;
+ flex-direction: row;
+
+ align-items: center;
+
+ margin: 0 0.25rem 0.25rem 0.25rem;
+
+ .message-actions-container {
+ display: none;
+ flex-direction: row;
+ align-items: center;
+
+ a, button {
+ display: flex;
+ }
+
+ .delete-icon {
+ color: $danger;
+ }
+ }
+
+ &:hover {
+ .message-actions-container {
+ display: flex;
+ }
+ }
+
+ p {
+ max-width: 50vw;
+ margin: 0; // reset margin
+ padding: 0.5rem 0.75rem;
+ border-radius: 1.5rem;
+
+ color: $white;
+ background-color: $primary;
+ }
+
+ &.chat-message-current-user {
+ display: flex;
+ flex-direction: row-reverse;
+ }
+
+ .message-body {
+ white-space: pre-line;
+ }
+
+ .replying-to-link {
+ border-bottom: 1px solid $white;
+ }
+
+ .replying-to {
+ color: $white;
+ font-size: 0.75rem;
+ }
+ }
+
+ .load-more-link-container, .start-of-conversation {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ margin-top: 0.25rem;
+ }
+ }
+
+ .channel-message-form-section {
+ flex: 0;
+ border-top: 1px solid $primary;
+
+ .channel-message-form {
+ display: flex;
+ flex-direction: row;
+
+ .channel-message-textarea {
+ flex: 1;
+ }
+
+ .channel-message-button-container {
+ min-width: 6rem;
+ flex: 0;
+ }
+ }
+
+ .reply-to-section {
+ display: none;
+ flex-direction: row;
+
+ p {
+ flex: 1;
+ }
+ }
+
+ .error-scroll-to-reply {
+ display: none;
+ color: $danger;
+ }
+
+ .join-channel-section {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ }
+ }
+
+ .message-sender {
+ font-size: 0.75rem;
+ margin-left: 44px; // align with message
+ }
+}
diff --git a/app/assets/stylesheets/chats.scss b/app/assets/stylesheets/chats.scss
index 013c701..6d929ff 100644
--- a/app/assets/stylesheets/chats.scss
+++ b/app/assets/stylesheets/chats.scss
@@ -1,43 +1,63 @@
-@import 'stream-chat-react/dist/scss/v2/index.scss';
-
body {
height: 100vh;
-}
+ max-height: 100vh;
-.chat-root {
display: flex;
+ flex-direction: column;
- .str-chat__channel {
- flex: 1;
+ .navbar {
+ flex: 0;
}
- .str-chat-channel-list {
- width: 20vw;
- }
-}
+ .chat-screen-container {
+ flex: 1;
-.str-chat {
- --str-chat__primary-color: #262626;
- --str-chat__own-message-bubble-background-color: #262626;
- --str-chat__own-message-bubble-color: white;
-}
+ .chat-message-section {
+ display: flex;
+ flex-direction: column;
-.str-chat__message--me {
- --str-chat__message-mention-color: white;
-}
+ border-left: 1px solid $primary;
-.str-chat__message-input {
- position: abolute;
- bottom: 0.5rem;
-}
+ &.no-chat {
+ justify-content: center;
+ align-items: center;
+ }
+ }
-.str-chat__list {
- min-height: 75vh;
- max-height: 75vh;
- overflow: scroll;
-}
+ .channel-list-container {
+ display: flex;
+ flex-direction: column;
+
+ .channel-list {
+ flex: 1;
+
+ display: flex;
+ flex-direction: column;
+
+ .channel-links-container {
+ flex: 1 0 0;
+ overflow: scroll;
+
+ display: flex;
+ flex-direction: column;
-.str-chat__thread {
- max-width: 30vw;
+ .channel-link {
+ border-bottom: 1px solid $primary;
+ text-decoration: none;
+
+ &:hover {
+ background-color: lighten($primary, 70%);
+ }
+ }
+ }
+
+ .channel-list-actions {
+ flex: 0;
+
+ display: grid;
+ }
+ }
+ }
+ }
}
diff --git a/app/assets/stylesheets/profiles.scss b/app/assets/stylesheets/profiles.scss
new file mode 100644
index 0000000..e69de29
diff --git a/app/assets/stylesheets/visas.scss b/app/assets/stylesheets/visas.scss
index be88ad9..7610a70 100644
--- a/app/assets/stylesheets/visas.scss
+++ b/app/assets/stylesheets/visas.scss
@@ -1,4 +1,4 @@
-.delete-country-button {
+.delete-button {
background: none;
color: inherit;
border: none;
diff --git a/app/controllers/channel_members_controller.rb b/app/controllers/channel_members_controller.rb
new file mode 100644
index 0000000..8a9accb
--- /dev/null
+++ b/app/controllers/channel_members_controller.rb
@@ -0,0 +1,62 @@
+class ChannelMembersController < ApplicationController
+ def create
+ @channel = Channel.find(create_channel_member_params[:channel_id])
+ @user = User.find(create_channel_member_params[:user_id])
+
+ @member = ChannelMember.new(
+ chat_channel: @channel,
+ user: @user
+ )
+
+ if @member.save
+ redirect_to @channel
+ else
+ flash[:error_join_channel] = "Couldn't join channel at this time. Please try again"
+
+ redirect_to @channel
+ end
+ end
+
+ def destroy
+ @member = ChannelMember.find(params[:id])
+
+ authorize(@member)
+ @member.destroy!
+
+ redirect_to chat_path
+ end
+
+ def update_last_active
+ @member = ChannelMember.find(params[:id])
+
+ authorize(@member)
+
+ previous_last_active = @member.last_active
+ @member.update!(last_active: Time.now)
+
+ respond_to do |format|
+ format.turbo_stream do
+ if previous_last_active < @member.chat_channel.last_action_at
+ render turbo_stream: turbo_stream.replace(
+ "channel-list",
+ partial: "chats/current_user_channel_list",
+ locals: {
+ user: current_user
+ }
+ )
+ end
+ end
+ end
+ rescue StandardError
+ render json: {}, status: 500
+ end
+
+ private
+
+ def create_channel_member_params
+ params.require(:channel_member).permit(
+ :channel_id,
+ :user_id
+ )
+ end
+end
diff --git a/app/controllers/channel_messages_controller.rb b/app/controllers/channel_messages_controller.rb
new file mode 100644
index 0000000..29bc884
--- /dev/null
+++ b/app/controllers/channel_messages_controller.rb
@@ -0,0 +1,165 @@
+class ChannelMessagesController < ApplicationController
+ include Pagy::Backend
+
+ def index
+ @channel = Channel.find(params[:channel_id])
+
+ authorize(@channel, :show?)
+
+ @pagy, @messages = pagy(
+ @channel.messages.order(created_at: :desc),
+ items: 50,
+ page: params[:page]
+ )
+
+ respond_to do |format|
+ format.turbo_stream do
+ render turbo_stream: turbo_stream.prepend(
+ "channel-messages",
+ partial: "channels/chat_messages",
+ locals: {
+ messages: @messages,
+ channel: @channel,
+ user: current_user
+ }
+ )
+ end
+ end
+ rescue Pagy::OverflowError
+ respond_to do |format|
+ format.turbo_stream do
+ render turbo_stream: turbo_stream.replace(
+ "load-more-link",
+ partial: "channels/start_of_conversation"
+ )
+ end
+ end
+ end
+
+ def create
+ @channel = Channel.find(params[:channel_id])
+ @message = ChannelMessage.new(
+ body: channel_message_params[:body],
+ reply_to_id: channel_message_params[:reply_to_id],
+ sender: current_user,
+ channel: @channel
+ )
+
+ authorize(@message)
+
+ if @message.save
+ # Update channel member
+ ChannelMember
+ .find_by!(
+ user: current_user,
+ chat_channel: @channel
+ )
+ .update!(
+ last_active: Time.now
+ )
+
+ # Update channel activity
+ @channel.update!(last_action_at: Time.now)
+
+ # Update users of the channel
+ # NOTE this should probably be a background job
+ @channel.channel_members.each do |channel_member|
+ user = channel_member.user
+ next if current_user == user
+
+ Turbo::StreamsChannel.broadcast_action_to(
+ "user-#{channel_member.user_id}-navbar-chat-link",
+ action: :replace,
+ target: "navbar-chat-link",
+ partial: "layouts/navbar/chat_link",
+ locals: {
+ user: user,
+ }
+ )
+
+ Turbo::StreamsChannel.broadcast_action_to(
+ "user-#{channel_member.user_id}-chat",
+ action: :replace,
+ target: "channel-list",
+ partial: "chats/current_user_channel_list",
+ locals: {
+ user: user,
+ }
+ )
+
+ Turbo::StreamsChannel.broadcast_action_to(
+ "user-#{channel_member.user_id}-channel-#{@channel.id}",
+ action: :append,
+ target: "channel-messages",
+ partial: "channels/chat_message",
+ locals: {
+ message: @message,
+ channel: @channel,
+ user: user
+ }
+ )
+ end
+ end
+
+ # If message.save fails, the partial handles the error messaging
+ # Else just re-render the section and show the sent message
+ respond_to do |format|
+ @messages = @channel.messages.order(created_at: :desc).limit(50)
+
+ format.turbo_stream
+ end
+ end
+
+ def destroy
+ @channel = Channel.find(params[:channel_id])
+ @message = @channel.messages.find_by!(
+ id: params[:id],
+ channel: @channel,
+ sender: current_user
+ )
+
+ authorize(@message)
+
+ @message.update!(deleted: true)
+
+ @channel.channel_members.each do |channel_member|
+ user = channel_member.user
+ next if current_user == user
+
+ Turbo::StreamsChannel.broadcast_action_to(
+ "user-#{channel_member.user_id}-channel-#{@channel.id}",
+ action: :replace,
+ target: "chat-message-#{@message.id}",
+ partial: "channels/chat_message",
+ locals: {
+ message: @message,
+ channel: @channel,
+ user: user
+ }
+ )
+ end
+
+ respond_to do |format|
+ format.turbo_stream do
+ render turbo_stream: turbo_stream.replace(
+ "chat-message-#{@message.id}",
+ partial: "channels/chat_message",
+ locals: {
+ message: @message,
+ channel: @channel,
+ user: current_user
+ }
+ )
+ end
+ end
+ end
+
+ private
+
+ def channel_message_params
+ params.require(:channel_message).permit(
+ :body,
+ :reply_to_id
+ )
+ end
+end
diff --git a/app/controllers/channels_controller.rb b/app/controllers/channels_controller.rb
new file mode 100644
index 0000000..09e8b55
--- /dev/null
+++ b/app/controllers/channels_controller.rb
@@ -0,0 +1,113 @@
+class ChannelsController < ApplicationController
+ include Pagy::Backend
+
+ layout false, only: [:show]
+
+ def show
+ @channel = Channel.find(params[:id])
+
+ authorize(@channel)
+
+ @channels = current_user.chat_channels
+ @messages = @channel.messages.order(created_at: :desc).limit(50)
+ @message = ChannelMessage.new
+
+ if @channel.include?(user: current_user)
+ # NOTE this should probably be a background job
+ @channel
+ .channel_members
+ .find_by!(user: current_user)
+ .update!(last_active: Time.now)
+
+ Turbo::StreamsChannel.broadcast_action_to(
+ "user-#{current_user}-navbar-chat-link",
+ action: :replace,
+ target: "navbar-chat-link",
+ partial: "layouts/navbar/chat_link",
+ locals: {
+ user: current_user
+ }
+ )
+ end
+
+ render "chats/show"
+ end
+
+ def new
+ @channel = Channel.new
+
+ authorize(@channel)
+ end
+
+ def create
+ @channel = Channel.new(
+ channel_params.merge({
+ last_action_at: Time.now
+ }),
+ )
+
+ authorize(@channel)
+
+ if @channel.save
+ # Add the admin who created the channel to it
+ ChannelMember.create!(
+ user: current_user,
+ chat_channel: @channel
+ )
+
+ redirect_to chat_path
+ else
+ flash[:error_create_channel] = "Couldn't create channel right now. Please try again"
+
+ render :new
+ end
+ end
+
+ def joinable
+ # TODO the query below isn't working - so write a less
+ # efficient query for now
+ #@channels = Channel
+ # .left_joins(:channel_members)
+ # .where.not(channel_members: { user_id: current_user.id })
+ current_user_channels = Channel
+ .joins(:channel_members)
+ .where("channel_members.user_id = ?", current_user.id)
+ .pluck(:id)
+
+ @channels = Channel
+ .where(id: Channel.pluck(:id) - current_user_channels)
+ .order(:last_action_at)
+
+ respond_to do |format|
+ format.turbo_stream do
+ render turbo_stream: turbo_stream.replace(
+ "channel-list",
+ partial: "chats/joinable_channel_list",
+ locals: { channels: @channels }
+ )
+ end
+ end
+ end
+
+ def current_user_list
+ respond_to do |format|
+ format.turbo_stream do
+ render turbo_stream: turbo_stream.replace(
+ "joinable-channel-list",
+ partial: "chats/current_user_channel_list",
+ locals: {
+ user: current_user
+ }
+ )
+ end
+ end
+ end
+
+ private
+
+ def channel_params
+ params.require(:channel).permit(
+ :name
+ )
+ end
+end
diff --git a/app/controllers/chats_controller.rb b/app/controllers/chats_controller.rb
index a7ecf48..c748eea 100644
--- a/app/controllers/chats_controller.rb
+++ b/app/controllers/chats_controller.rb
@@ -3,51 +3,36 @@
class ChatsController < ApplicationController
layout false, only: [:show]
- before_action :authenticate_subscription!
+ before_action :authenticate_subscription!, only: [:show]
def show
- if (ENV["STREAM_API_KEY"] && ENV["STREAM_API_SECRET"]).present?
- if current_user.stream_user_id.nil?
- @stream_user_id = SecureRandom.hex(16)
- @stream_user_token = StreamChatClient.create_stream_user(
- id: @stream_user_id
- )
+ add_user_to_default_channels
+
+ @channels = current_user.chat_channels
+ end
- # TODO error handling
- current_user.update!(
- stream_user_id: @stream_user_id,
- stream_user_token: @stream_user_token
+ def navbar_link
+ respond_to do |format|
+ format.turbo_stream do
+ render turbo_stream: turbo_stream.replace(
+ "navbar-chat-link",
+ partial: "layouts/navbar/chat_link",
+ locals: {
+ user: current_user
+ }
)
- else
- @stream_user_id = current_user.stream_user_id
- @stream_user_token = current_user.stream_user_token
end
-
- add_current_user_to_channels
- else
- @stream_env_vars_missing = true
end
end
private
- def add_current_user_to_channels
- [
- { type: "messaging", id: "general" },
- { type: "messaging", id: "feedback-and-requests" },
- { type: "messaging", id: "bugs" },
- ].each do |channel|
- channel = StreamChatClient.get_channel(
- type: channel[:type],
- channel_id: channel[:id],
- )
- unless StreamChatClient.channel_include?(
- channel: channel,
- user_id: current_user.stream_user_id
- )
- StreamChatClient.add_member(
- channel: channel,
- user_id: current_user.stream_user_id
+ def add_user_to_default_channels
+ if current_user.chat_channels.empty?
+ Channel::DEFAULT_CHAT_CHANNELS.each do |channel_name|
+ ChannelMember.create!(
+ chat_channel: Channel.find_by!(name: channel_name),
+ user: current_user
)
end
end
diff --git a/app/controllers/profile_pictures_controller.rb b/app/controllers/profile_pictures_controller.rb
new file mode 100644
index 0000000..b314996
--- /dev/null
+++ b/app/controllers/profile_pictures_controller.rb
@@ -0,0 +1,71 @@
+class ProfilePicturesController < ApplicationController
+ def create
+ @user = User.find(params[:user_id])
+
+ authorize(@user, :edit?)
+
+ @profile_picture = ProfilePicture.new(
+ user: @user,
+ image: profile_picture_params[:image]
+ )
+
+ if @profile_picture.save
+ redirect_to profile_path
+ else
+ flash[:error_upload_profile_picture] = "Couldn't upload profile picture. Please try again"
+
+ redirect_to profile_path
+ end
+ end
+
+ def update
+ @user = User.find(params[:user_id])
+
+ authorize(@user, :edit?)
+
+ @user.profile_picture.assign_attributes(
+ image: profile_picture_params[:image]
+ )
+
+ if @user.profile_picture.save
+ redirect_to profile_path
+ else
+ flash[:error_upload_profile_picture] = "Couldn't upload profile picture. Please try again"
+
+ redirect_to profile_path
+ end
+ end
+
+ def upload_modal
+ @user = User.find(params[:user_id])
+
+ authorize(@user, :edit?)
+
+ if @user.profile_picture.present?
+ @profile_picture = @user.profile_picture
+ else
+ @profile_picture = ProfilePicture.new
+ end
+
+ respond_to do |format|
+ format.turbo_stream do
+ render turbo_stream: turbo_stream.append(
+ "site-modals",
+ partial: "profile_pictures/upload_modal",
+ locals: {
+ user: @user,
+ profile_picture: @profile_picture
+ }
+ )
+ end
+ end
+ end
+
+ private
+
+ def profile_picture_params
+ params.require(:profile_picture).permit(
+ :image
+ )
+ end
+end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index de6be79..5557d97 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -1,2 +1,12 @@
module ApplicationHelper
+ def helper_user_initials(user:)
+ split_display_name = user.display_name.upcase.split(" ")
+ if split_display_name.length >= 2
+ "#{split_display_name[0][0]}#{split_display_name[1][0]}"
+ elsif split_display_name[0].length >= 2
+ "#{split_display_name[0][0]}#{split_display_name[0][1]}"
+ else
+ "#{split_display_name[0]}"
+ end
+ end
end
diff --git a/app/helpers/chats_helper.rb b/app/helpers/chats_helper.rb
new file mode 100644
index 0000000..fa7c116
--- /dev/null
+++ b/app/helpers/chats_helper.rb
@@ -0,0 +1,9 @@
+module ChatsHelper
+ def helper_short_message(message:)
+ if message.length >= 50
+ message[0..47] + "..."
+ else
+ message
+ end
+ end
+end
diff --git a/app/helpers/profiles_helper.rb b/app/helpers/profiles_helper.rb
new file mode 100644
index 0000000..4e43050
--- /dev/null
+++ b/app/helpers/profiles_helper.rb
@@ -0,0 +1,2 @@
+module ProfilesHelper
+end
diff --git a/app/javascript/application.js b/app/javascript/application.js
index 902050a..3016c21 100644
--- a/app/javascript/application.js
+++ b/app/javascript/application.js
@@ -2,5 +2,3 @@
import "@hotwired/turbo-rails"
import "./controllers"
import * as bootstrap from "bootstrap"
-
-import "./components"
diff --git a/app/javascript/components/Chat/index.jsx b/app/javascript/components/Chat/index.jsx
deleted file mode 100644
index 43e4698..0000000
--- a/app/javascript/components/Chat/index.jsx
+++ /dev/null
@@ -1,78 +0,0 @@
-import React, { useState, useEffect } from "react";
-import { createRoot } from "react-dom/client";
-import { StreamChat } from 'stream-chat';
-import {
- Chat as StreamChatComponent,
- Channel,
- ChannelHeader,
- ChannelList,
- MessageList,
- MessageInput,
- Thread,
- Window,
-} from 'stream-chat-react';
-
-const rootElement = document.getElementById("chat-root");
-const userId = rootElement?.dataset.streamUserId;
-const displayName = rootElement?.dataset.displayName;
-const userToken = rootElement?.dataset.streamUserToken;
-
-const filters = { type: 'messaging', members: { $in: [userId]} };
-const options = { state: true, presence: true };
-const sort = { last_message_at: -1 };
-
-const Chat = () => {
- const [client, setClient] = useState(null);
-
- useEffect(() => {
- const newClient = new StreamChat('s3u4gjg6hnj2');
-
- const handleConnectionChange = ({ online = false }) => {
- if (!online) return console.log('connection lost');
- setClient(newClient);
- };
-
- newClient.on('connection.changed', handleConnectionChange);
-
- newClient.connectUser(
- {
- id: userId,
- name: displayName,
- },
- userToken,
- );
-
- return () => {
- newClient.off('connection.changed', handleConnectionChange);
- newClient.disconnectUser().then(() => console.log('connection closed'));
- };
- }, []);
-
- if (!client) return null;
-
- return (
-
+ Start of conversation +
diff --git a/app/views/channels/new.html.erb b/app/views/channels/new.html.erb new file mode 100644 index 0000000..0886bb1 --- /dev/null +++ b/app/views/channels/new.html.erb @@ -0,0 +1,29 @@ ++ Looks like you've joined all available channels. +
++ <%= channel.name %> + <%= user.unread_message_count(channel: channel) != 0 ? "(#{user.unread_message_count(channel: channel)})".html_safe : nil %> +
++ <% if channel.messages.any? %> + <%= helper_short_message(message: channel.latest_message.body) %> + <% else %> + No messages sent yet + <% end %> +
+
-
- ENV["STREAM_API_KEY"] and/or ENV["STREAM_API_SECRET"] not set.
-
-
- Please check your environment configuration and try again.
-