Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c505eef43 | ||
|
|
4429e95f2b | ||
|
|
61c07472e0 | ||
|
|
09969af10c | ||
|
|
f830821e0a | ||
|
|
bc6e3e598f | ||
|
|
683f9d9a28 | ||
|
|
f9bf0883c6 | ||
|
|
ad8ba299c9 | ||
|
|
3ae64822e1 | ||
|
|
a49aaee9cf |
@@ -1,7 +1,7 @@
|
||||
class Api::V1::Accounts::NotificationsController < Api::V1::Accounts::BaseController
|
||||
RESULTS_PER_PAGE = 15
|
||||
|
||||
before_action :fetch_notification, only: [:update]
|
||||
before_action :fetch_notification, only: [:update, :destroy]
|
||||
before_action :set_primary_actor, only: [:read_all]
|
||||
before_action :set_current_page, only: [:index]
|
||||
|
||||
@@ -28,6 +28,11 @@ class Api::V1::Accounts::NotificationsController < Api::V1::Accounts::BaseContro
|
||||
render json: @notification
|
||||
end
|
||||
|
||||
def destroy
|
||||
@notification.destroy
|
||||
head :ok
|
||||
end
|
||||
|
||||
def unread_count
|
||||
@unread_count = current_user.notifications.where(account_id: current_account.id, read_at: nil).count
|
||||
render json: @unread_count
|
||||
|
||||
@@ -61,4 +61,9 @@ module PortalHelper
|
||||
def render_category_content(content)
|
||||
ChatwootMarkdownRenderer.new(content).render_markdown_to_plain_text
|
||||
end
|
||||
|
||||
def thumbnail_bg_color(username)
|
||||
colors = ['#6D95BA', '#A4C3C3', '#E19191']
|
||||
colors[username.length % colors.size]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -144,7 +144,6 @@ import { FEATURE_FLAGS } from 'dashboard/featureFlags';
|
||||
import {
|
||||
ALLOWED_FILE_TYPES,
|
||||
ALLOWED_FILE_TYPES_FOR_TWILIO_WHATSAPP,
|
||||
ALLOWED_FILE_TYPES_FOR_LINE,
|
||||
} from 'shared/constants/messages';
|
||||
import VideoCallButton from '../VideoCallButton.vue';
|
||||
import AIAssistanceButton from '../AIAssistanceButton.vue';
|
||||
@@ -271,9 +270,6 @@ export default {
|
||||
return this.showFileUpload || this.isNote;
|
||||
},
|
||||
showAudioRecorderButton() {
|
||||
if (this.isALineChannel) {
|
||||
return false;
|
||||
}
|
||||
// Disable audio recorder for safari browser as recording is not supported
|
||||
const isSafari = /^((?!chrome|android|crios|fxios).)*safari/i.test(
|
||||
navigator.userAgent
|
||||
@@ -295,9 +291,6 @@ export default {
|
||||
if (this.isATwilioWhatsAppChannel) {
|
||||
return ALLOWED_FILE_TYPES_FOR_TWILIO_WHATSAPP;
|
||||
}
|
||||
if (this.isALineChannel) {
|
||||
return ALLOWED_FILE_TYPES_FOR_LINE;
|
||||
}
|
||||
return ALLOWED_FILE_TYPES;
|
||||
},
|
||||
enableDragAndDrop() {
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
:message="inReplyTo"
|
||||
:message-type="data.message_type"
|
||||
:parent-has-attachments="hasAttachments"
|
||||
@click="navigateToMessage"
|
||||
/>
|
||||
<bubble-text
|
||||
v-if="data.content"
|
||||
@@ -519,6 +520,13 @@ export default {
|
||||
this.showBackgroundHighlight = false;
|
||||
}, HIGHLIGHT_TIMER);
|
||||
},
|
||||
async navigateToMessage() {
|
||||
this.$nextTick(() => {
|
||||
bus.$emit(BUS_EVENTS.SCROLL_TO_MESSAGE, {
|
||||
messageId: this.inReplyToMessageId,
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -377,12 +377,19 @@ export default {
|
||||
removeBusListeners() {
|
||||
bus.$off(BUS_EVENTS.SCROLL_TO_MESSAGE, this.onScrollToMessage);
|
||||
},
|
||||
onScrollToMessage({ messageId = '' } = {}) {
|
||||
async onScrollToMessage({ messageId }) {
|
||||
// TODO: check if the message is already loaded
|
||||
await this.$store.dispatch('setActiveChat', {
|
||||
data: this.currentChat,
|
||||
after: messageId,
|
||||
force: true,
|
||||
});
|
||||
|
||||
this.$nextTick(() => {
|
||||
const messageElement = document.getElementById('message' + messageId);
|
||||
if (messageElement) {
|
||||
this.isProgrammaticScroll = true;
|
||||
messageElement.scrollIntoView({ behavior: 'smooth' });
|
||||
messageElement.scrollIntoView();
|
||||
this.fetchPreviousMessages();
|
||||
} else {
|
||||
this.scrollToBottom();
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
v-if="shouldShowReplyToMessage"
|
||||
:message="inReplyTo"
|
||||
@dismiss="resetReplyToMessage"
|
||||
@navigate-to-message="navigateToMessage"
|
||||
/>
|
||||
<canned-response
|
||||
v-if="showMentions && hasSlashCommand"
|
||||
@@ -392,8 +393,7 @@ export default {
|
||||
this.isAPIInbox ||
|
||||
this.isAnEmailChannel ||
|
||||
this.isASmsInbox ||
|
||||
this.isATelegramChannel ||
|
||||
this.isALineChannel
|
||||
this.isATelegramChannel
|
||||
);
|
||||
},
|
||||
replyButtonLabel() {
|
||||
@@ -1159,6 +1159,12 @@ export default {
|
||||
LocalStorage.deleteFromJsonStore(replyStorageKey, this.conversationId);
|
||||
bus.$emit(BUS_EVENTS.TOGGLE_REPLY_TO_MESSAGE);
|
||||
},
|
||||
navigateToMessage(messageId) {
|
||||
bus.$emit(BUS_EVENTS.SCROLL_TO_MESSAGE, {
|
||||
messageId,
|
||||
behavior: 'instant',
|
||||
});
|
||||
},
|
||||
onNewConversationModalActive(isActive) {
|
||||
// Issue is if the new conversation modal is open and we drag and drop the file
|
||||
// then the file is not getting attached to the new conversation modal
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<div
|
||||
class="reply-editor bg-slate-50 dark:bg-slate-800 rounded-md py-1 pl-2 pr-1 text-xs tracking-wide mt-2 flex items-center gap-1.5 -mx-2"
|
||||
class="reply-editor bg-slate-50 dark:bg-slate-800 rounded-md py-1 pl-2 pr-1 text-xs tracking-wide mt-2 flex items-center gap-1.5 -mx-2 cursor-pointer"
|
||||
@click="$emit('navigate-to-message', message.id)"
|
||||
>
|
||||
<fluent-icon class="flex-shrink-0 icon" icon="arrow-reply" size="14" />
|
||||
<div class="flex-grow gap-1 mt-px text-xs truncate">
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<template>
|
||||
<div
|
||||
class="px-2 py-1.5 rounded-sm min-w-[10rem] mb-2"
|
||||
class="px-2 py-1.5 -mx-2 rounded-sm min-w-[15rem] mb-2 cursor-pointer"
|
||||
:class="{
|
||||
'bg-slate-50 dark:bg-slate-600 dark:text-slate-50':
|
||||
messageType === MESSAGE_TYPE.INCOMING,
|
||||
'bg-woot-600 text-woot-50': messageType === MESSAGE_TYPE.OUTGOING,
|
||||
'-mx-2': !parentHasAttachments,
|
||||
}"
|
||||
@click="$emit('click')"
|
||||
>
|
||||
<message-preview
|
||||
:message="message"
|
||||
|
||||
@@ -7,7 +7,11 @@
|
||||
}"
|
||||
>
|
||||
<div v-if="!isEmail" v-dompurify-html="message" class="text-content" />
|
||||
<letter v-else class="text-content" :html="message" />
|
||||
<letter
|
||||
v-else
|
||||
class="text-content bg-white dark:bg-white text-slate-900 dark:text-slate-900 p-2 rounded-[4px]"
|
||||
:html="message"
|
||||
/>
|
||||
<button
|
||||
v-if="showQuoteToggle"
|
||||
class="text-slate-300 dark:text-slate-300 cursor-pointer text-xs py-1"
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
id="file"
|
||||
ref="file"
|
||||
type="file"
|
||||
accept="image/png, image/jpeg, image/gif"
|
||||
accept="image/png, image/jpeg, image/jpg, image/gif, image/webp"
|
||||
@change="handleImageUpload"
|
||||
/>
|
||||
<slot />
|
||||
|
||||
@@ -163,14 +163,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
const { messageId } = this.$route.query;
|
||||
this.$store
|
||||
.dispatch('setActiveChat', {
|
||||
data: selectedConversation,
|
||||
after: messageId,
|
||||
})
|
||||
.then(() => {
|
||||
bus.$emit(BUS_EVENTS.SCROLL_TO_MESSAGE, { messageId });
|
||||
});
|
||||
bus.$emit(BUS_EVENTS.SCROLL_TO_MESSAGE, { messageId });
|
||||
} else {
|
||||
this.$store.dispatch('clearSelectedState');
|
||||
}
|
||||
|
||||
+16
-9
@@ -157,16 +157,23 @@ export default {
|
||||
return this.metaTags.map(item => item.name);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
article: {
|
||||
handler() {
|
||||
if (!isEmptyObject(this.article.meta || {})) {
|
||||
const {
|
||||
meta: { title = '', description = '', tags = [] },
|
||||
} = this.article;
|
||||
this.metaTitle = title;
|
||||
this.metaDescription = description;
|
||||
this.metaTags = this.formattedTags({ tags });
|
||||
}
|
||||
},
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (!isEmptyObject(this.article.meta || {})) {
|
||||
const {
|
||||
meta: { title = '', description = '', tags = [] },
|
||||
} = this.article;
|
||||
this.metaTitle = title;
|
||||
this.metaDescription = description;
|
||||
this.metaTags = this.formattedTags({ tags });
|
||||
}
|
||||
|
||||
this.saveArticle = debounce(
|
||||
() => {
|
||||
this.$emit('save-article', {
|
||||
|
||||
@@ -160,10 +160,10 @@ const actions = {
|
||||
});
|
||||
},
|
||||
|
||||
async setActiveChat({ commit, dispatch }, { data, after }) {
|
||||
async setActiveChat({ commit, dispatch }, { data, after, force = false }) {
|
||||
commit(types.SET_CURRENT_CHAT_WINDOW, data);
|
||||
commit(types.CLEAR_ALL_MESSAGES_LOADED);
|
||||
if (data.dataFetched === undefined) {
|
||||
if (data.dataFetched === undefined || force) {
|
||||
try {
|
||||
await dispatch('fetchPreviousMessages', {
|
||||
after,
|
||||
|
||||
@@ -55,8 +55,6 @@ export const ALLOWED_FILE_TYPES_FOR_TWILIO_WHATSAPP =
|
||||
'audio/mpeg, audio/opus, audio/ogg, audio/amr,' +
|
||||
'video/mp4,' +
|
||||
'application/pdf,';
|
||||
// https://developers.line.biz/en/reference/messaging-api/#image-message, https://developers.line.biz/en/reference/messaging-api/#video-message
|
||||
export const ALLOWED_FILE_TYPES_FOR_LINE = 'image/png, image/jpeg,video/mp4';
|
||||
|
||||
export const CSAT_RATINGS = [
|
||||
{
|
||||
|
||||
@@ -6,8 +6,7 @@ class Line::SendOnLineService < Base::SendOnChannelService
|
||||
end
|
||||
|
||||
def perform_reply
|
||||
response = channel.client.push_message(message.conversation.contact_inbox.source_id, build_payload)
|
||||
|
||||
response = channel.client.push_message(message.conversation.contact_inbox.source_id, [{ type: 'text', text: message.content }])
|
||||
return if response.blank?
|
||||
|
||||
parsed_json = JSON.parse(response.body)
|
||||
@@ -21,36 +20,6 @@ class Line::SendOnLineService < Base::SendOnChannelService
|
||||
end
|
||||
end
|
||||
|
||||
def build_payload
|
||||
if message.content && message.attachments.any?
|
||||
[text_message, *attachments]
|
||||
elsif message.content.nil? && message.attachments.any?
|
||||
attachments
|
||||
else
|
||||
text_message
|
||||
end
|
||||
end
|
||||
|
||||
def attachments
|
||||
message.attachments.map do |attachment|
|
||||
# Support only image and video for now
|
||||
next unless attachment.file_type == 'image' || attachment.file_type == 'video'
|
||||
|
||||
{
|
||||
type: attachment.file_type,
|
||||
originalContentUrl: attachment.download_url,
|
||||
previewImageUrl: attachment.download_url
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def text_message
|
||||
{
|
||||
type: 'text',
|
||||
text: message.content
|
||||
}
|
||||
end
|
||||
|
||||
# https://developers.line.biz/en/reference/messaging-api/#error-responses
|
||||
def external_error(error)
|
||||
# Message containing information about the error. See https://developers.line.biz/en/reference/messaging-api/#error-messages
|
||||
|
||||
@@ -108,7 +108,9 @@ class Twilio::IncomingMessageService
|
||||
return if params[:MediaUrl0].blank?
|
||||
|
||||
attachment_file = Down.download(
|
||||
params[:MediaUrl0]
|
||||
params[:MediaUrl0],
|
||||
# https://support.twilio.com/hc/en-us/articles/223183748-Protect-Media-Access-with-HTTP-Basic-Authentication-for-Programmable-Messaging
|
||||
http_basic_authentication: [twilio_channel.account_sid, twilio_channel.auth_token || twilio_channel.api_key_sid]
|
||||
)
|
||||
|
||||
attachment = @message.attachments.new(
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><path fill="" d="M6 1a2 2 0 1 0 0 4a2 2 0 0 0 0-4Zm2.5 5h-5A1.5 1.5 0 0 0 2 7.5c0 1.116.459 2.01 1.212 2.615C3.953 10.71 4.947 11 6 11c1.053 0 2.047-.29 2.788-.885C9.54 9.51 10 8.616 10 7.5A1.5 1.5 0 0 0 8.5 6Z"/></svg>
|
||||
|
After Width: | Height: | Size: 303 B |
@@ -0,0 +1,24 @@
|
||||
<% author_count = category.articles.published.order(position: :asc).map(&:author).uniq.size %>
|
||||
<% if author_count > 0 %>
|
||||
<div class="flex flex-row items-center gap-1">
|
||||
<div class="flex flex-row items-center -space-x-2">
|
||||
<% category.articles.published.order(position: :asc).map(&:author).uniq.take(3).each do |author| %>
|
||||
<%= render "public/api/v1/portals/thumbnail", author: author, size: 5 %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<% first_author = category.articles.published.order(position: :asc).map(&:author).uniq.first.name %>
|
||||
<% author_text = author_count > 1 ? "#{author_count} #{I18n.t('public_portal.common.authors')}" : "#{author_count} #{I18n.t('public_portal.common.author')}" %>
|
||||
<% other_authors_count = author_count - 1 %>
|
||||
<% other_authors_text = other_authors_count > 1 ? I18n.t('public_portal.common.others') : I18n.t('public_portal.common.other') %>
|
||||
|
||||
<span class="<%= show_expanded ? 'text-base' : 'text-sm' %> font-medium text-slate-600 dark:text-slate-400">
|
||||
<% if show_expanded %>
|
||||
<%= "#{I18n.t('public_portal.common.by')} #{first_author}" %>
|
||||
<%= other_authors_count.positive? ? " and #{other_authors_count} #{other_authors_text}" : '' %>
|
||||
<% else %>
|
||||
<%= author_text %>
|
||||
<% end %>
|
||||
</span>
|
||||
</div>
|
||||
<% end %>
|
||||
@@ -34,7 +34,7 @@
|
||||
<% else %>
|
||||
<% category.articles.published.order(position: :asc).take(5).each do |article| %>
|
||||
<a class="leading-7 text-slate-700 dark:text-slate-100" href="<%= generate_article_link(portal.slug, article.slug, @theme_from_params, @is_plain_layout_enabled) %>">
|
||||
<div id="<%= !@is_plain_layout_enabled ? 'category-item' : '' %>" class="flex justify-between hover:cursor-pointer items-start py-1 rounded-lg gap-3 <%= !@is_plain_layout_enabled ? 'px-2' : 'hover:underline' %>">
|
||||
<div id="<%= !@is_plain_layout_enabled ? 'category-item' : '' %>" class="flex justify-between hover:cursor-pointer items-start py-1 rounded-lg gap-6 <%= !@is_plain_layout_enabled ? 'px-2' : 'hover:underline' %>">
|
||||
<%= article.title %>
|
||||
<span class="flex items-center font-normal mt-1.5">
|
||||
<%= render partial: 'icons/chevron-right' %>
|
||||
@@ -45,7 +45,9 @@
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="flex justify-between flex-row items-center <%= !@is_plain_layout_enabled && 'px-2' %>">
|
||||
<div>
|
||||
<div class="flex flex-row items-center gap-1">
|
||||
<%= render "public/api/v1/portals/authors", category: category, show_expanded: false %>
|
||||
<span class="text-slate-600 dark:text-slate-400">•</span>
|
||||
<span class="text-sm font-medium text-slate-600 dark:text-slate-400"><%= render 'public/api/v1/portals/article_count', article_count: category.articles.published.order(position: :asc).size %></span>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<div class="grid grid-cols-1 gap-2 md:grid-cols-2 gap-x-2 gap-y-2">
|
||||
<% featured_articles.each do |article| %>
|
||||
<a class="leading-7 text-slate-700 dark:text-slate-100" href="<%= generate_article_link(portal.slug, article.slug, @theme_from_params, @is_plain_layout_enabled) %>">
|
||||
<div id="category-item" class="flex items-start justify-between gap-3 px-2 py-1 rounded-lg">
|
||||
<div id="category-item" class="flex items-start justify-between gap-6 px-2 py-1 rounded-lg">
|
||||
<%= article.title %>
|
||||
<span class="flex items-center font-normal mt-1.5">
|
||||
<%= render partial: 'icons/chevron-right' %>
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<% if author&.avatar_url&.present? %>
|
||||
<img src="<%= url_for(author.avatar_url) %>" alt="<%= author.name %>" class="w-<%= size %> h-<%= size %> rounded-full border border-solid border-white dark:border-slate-900">
|
||||
<% else %>
|
||||
<div class="w-<%= size %> h-<%= size %> rounded-full [&>svg]:opacity-70 border border-solid fill-white dark:fill-slate-900 border-white dark:border-slate-900 flex justify-center items-center" style="background-color: <%= thumbnail_bg_color(author&.available_name) %>;">
|
||||
<%= render partial: 'icons/user' %>
|
||||
</div>
|
||||
<% end %>
|
||||
@@ -30,11 +30,9 @@
|
||||
</h1>
|
||||
<div class="flex flex-col items-start justify-between w-full pt-6 md:flex-row md:items-center">
|
||||
<div class="flex items-start space-x-1">
|
||||
<% if article.author&.avatar_url&.present? %>
|
||||
<div class="pr-px mt-0.5 min-w-[20px] min-h-[20px]">
|
||||
<img src="<%= article.author.avatar_url %>" alt="<%= article.author.display_name %>" class="w-5 h-5 border rounded-full">
|
||||
</div>
|
||||
<% end %>
|
||||
<div class="pr-px mt-0.5 min-w-[20px] min-h-[20px]">
|
||||
<%= render "public/api/v1/portals/thumbnail", author: article.author, size: 5 %>
|
||||
</div>
|
||||
<span class="flex items-center text-base font-medium text-slate-600 dark:text-slate-400">By <%= article.author.available_name %> • <%= I18n.t('public_portal.common.last_updated_on', last_updated_on: article.updated_at.strftime("%b %d, %Y")) %></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
<span class="font-medium text-slate-700 dark:text-slate-200 text-base leading-5"><%= category.description %></span>
|
||||
<% end %>
|
||||
</div>
|
||||
<span class="flex items-center text-base text-slate-600 dark:text-slate-400 font-medium"><%= render 'public/api/v1/portals/article_count', article_count: category.articles.published.size %></span>
|
||||
<div class="flex flex-row items-center gap-1">
|
||||
<%= render "public/api/v1/portals/authors", category: category, show_expanded: true %>
|
||||
<span class="text-slate-600 dark:text-slate-400">•</span>
|
||||
<span class="flex items-center text-base text-slate-600 dark:text-slate-400 font-medium"><%= render 'public/api/v1/portals/article_count', article_count: category.articles.published.size %></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -29,10 +29,18 @@
|
||||
>
|
||||
<div class="flex flex-col gap-5">
|
||||
<div class="flex flex-col gap-1">
|
||||
<h3 id="<%= !@is_plain_layout_enabled ? 'category-name' : '' %>" class="text-lg text-slate-900 dark:text-slate-50 font-semibold <%= @is_plain_layout_enabled ? 'group-hover:underline' : '' %>"><%= article.title %></h3>
|
||||
<p class="text-base font-normal text-slate-500 dark:text-slate-200 line-clamp-1 break-all"><%= render_category_content(article.content) %></p>
|
||||
<h3 id="<%= !@is_plain_layout_enabled ? 'category-name' : '' %>" class="text-lg text-slate-900 tracking-[0.28px] dark:text-slate-50 font-semibold <%= @is_plain_layout_enabled ? 'group-hover:underline' : '' %>"><%= article.title %></h3>
|
||||
<p class="text-base font-normal text-slate-600 dark:text-slate-200 line-clamp-1 break-all"><%= render_category_content(article.content) %></p>
|
||||
</div>
|
||||
<div class="flex flex-row items-center gap-1">
|
||||
<div class="flex flex-row items-center gap-1">
|
||||
<% author = article.author %>
|
||||
<%= render "public/api/v1/portals/thumbnail", author: author, size: 5 %>
|
||||
<span class="text-sm text-slate-600 dark:text-slate-400 font-medium flex items-center"><%= "#{I18n.t('public_portal.common.by')} #{author.name}" %></span>
|
||||
</div>
|
||||
<span class="text-slate-600 dark:text-slate-400">•</span>
|
||||
<span class="text-sm text-slate-600 dark:text-slate-400 font-medium flex items-center"><%= I18n.t('public_portal.common.last_updated_on', last_updated_on: article.updated_at.strftime("%b %d, %Y")) %></span>
|
||||
</div>
|
||||
<span class="text-sm text-slate-600 dark:text-slate-400 font-medium flex items-center"><%= I18n.t('public_portal.common.last_updated_on', last_updated_on: article.updated_at.strftime("%b %d, %Y")) %></span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -215,6 +215,11 @@ en:
|
||||
view_all_articles: View all
|
||||
article: article
|
||||
articles: articles
|
||||
author: author
|
||||
authors: authors
|
||||
other: other
|
||||
others: others
|
||||
by: By
|
||||
no_articles: There are no articles here
|
||||
footer:
|
||||
made_with: Made with
|
||||
|
||||
+1
-1
@@ -166,7 +166,7 @@ Rails.application.routes.draw do
|
||||
end
|
||||
end
|
||||
|
||||
resources :notifications, only: [:index, :update] do
|
||||
resources :notifications, only: [:index, :update, :destroy] do
|
||||
collection do
|
||||
post :read_all
|
||||
get :unread_count
|
||||
|
||||
@@ -127,4 +127,30 @@ RSpec.describe 'Notifications API', type: :request do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE /api/v1/accounts/{account.id}/notifications/:id' do
|
||||
let(:admin) { create(:user, account: account, role: :administrator) }
|
||||
let!(:notification) { create(:notification, account: account, user: admin) }
|
||||
|
||||
context 'when it is an unauthenticated user' do
|
||||
it 'returns unauthorized' do
|
||||
delete "/api/v1/accounts/#{account.id}/notifications/#{notification.id}"
|
||||
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when it is an authenticated user' do
|
||||
let(:admin) { create(:user, account: account, role: :administrator) }
|
||||
|
||||
it 'deletes the notification' do
|
||||
delete "/api/v1/accounts/#{account.id}/notifications/#{notification.id}",
|
||||
headers: admin.create_new_auth_token,
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(Notification.count).to eq(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -246,4 +246,16 @@ describe PortalHelper do
|
||||
expect(helper.render_category_content(markdown_content)).to eq(plain_text_content)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#thumbnail_bg_color' do
|
||||
it 'returns the correct color based on username length' do
|
||||
expect(helper.thumbnail_bg_color('')).to eq('#6D95BA') # Length 0, so index is 0
|
||||
expect(helper.thumbnail_bg_color('Joe')).to eq('#6D95BA') # Length 3, so index is 0
|
||||
expect(helper.thumbnail_bg_color('John')).to eq('#A4C3C3') # Length 4, so index is 1
|
||||
expect(helper.thumbnail_bg_color('Jane james')).to eq('#A4C3C3') # Length 10, so index is 1
|
||||
expect(helper.thumbnail_bg_color('Jane_123')).to eq('#E19191') # Length 8, so index is 2
|
||||
expect(helper.thumbnail_bg_color('AlexanderTheGreat')).to eq('#E19191') # Length 17, so index is 2
|
||||
expect(helper.thumbnail_bg_color('Reginald John Sans')).to eq('#6D95BA') # Length 18, so index is 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user