Skip to content

Commit

Permalink
Merge pull request #26 from mudmykola/feature/CRJ_optimization
Browse files Browse the repository at this point in the history
 update(json): updated the list of cartoons
  • Loading branch information
mudmykola authored Sep 13, 2024
2 parents 681201a + fd378d3 commit c11185d
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 72 deletions.
26 changes: 13 additions & 13 deletions .firebase/hosting.ZGlzdA.cache
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
index.html,1725781490346,59577291b801e90646eef6fe3012872b1192012537baa7907654d55383187d19
assets/fa-v4compatibility-BX8XWJtE.woff2,1725781490346,8945f97a3a1bbd891397f2f7900844dbf1808e9e3924900a31d4acafb11a9db2
data/cartoons.json,1725781490034,0a612d3f9f67b6cc4880a3b4dda7463f88df97d9fed28738159fef6af3c97f96
assets/fa-v4compatibility-B9MWI-E6.ttf,1725781490346,d5040b176e185deb1562d78b306cffe3336502f43ad77337eb9b8e464e1ab757
assets/FavoriteComponent-CvQGpqgv.js,1725781490347,761549a095836de93560964a12856d6203d0cdf3bb078a5d8f9b5a82054c95fc
assets/fa-regular-400-DgEfZSYE.woff2,1725781490346,fb31b3f693b818441c452f39a682d8ad81967e3474b6a2266ddb885177cd98d1
assets/fa-regular-400-Bf3rG5Nx.ttf,1725781490346,61e9221981e1ad8a27ea29b9aed2d9d1615450c3f99b3a6175c2ef238fa67eb4
assets/fa-brands-400-O7nZalfM.woff2,1725781490344,5446f4c298ee6694ba92500f91741668e1ff9c8c08cb018a4f414fb663acae9e
assets/fa-solid-900-DOQJEhcS.woff2,1725781490346,7a9c4ef0dd299dfd976428e4cb9c86fc13ebd4eed7604f035476e69af3f56b9d
assets/fa-brands-400-Dur5g48u.ttf,1725781490346,8a036bca81921a2f3fc90c860fde21f0e8066cdf3507cf9999dd2a47cac380b1
assets/index-DTRffuzk.css,1725781490347,764766dafbe4568dc75f3e2def3adeff518cf7f79041485b6ba51743808b1995
assets/index-BLQvRMyn.js,1725781490347,285b70fcadad118d2aaaa52653a0d6035bcd6c3c7cdc4828a003c6112f25bdc8
assets/fa-solid-900-BV3CbEM2.ttf,1725781490346,af1fb4d30a49d562da382fdc49f386e2c8075bbbe26a5ecbbcdcf8994aaa423d
index.html,1726206215334,25393176ff3ddf28ec46e13af8d4e432a54fdf29b5bf806d3b2c57a1c8b6fba5
assets/fa-v4compatibility-BX8XWJtE.woff2,1726206215334,8945f97a3a1bbd891397f2f7900844dbf1808e9e3924900a31d4acafb11a9db2
data/cartoons.json,1726206215003,7fc09526226ac347c18f1e3e4506c1748c9eb1da294b7c4d521017df05a2be65
assets/fa-v4compatibility-B9MWI-E6.ttf,1726206215333,d5040b176e185deb1562d78b306cffe3336502f43ad77337eb9b8e464e1ab757
assets/FavoriteComponent-CU74GVyb.js,1726206215334,38dd6cb7bdbdab1751a0d6dc75ee681f148b5f6a32cca87d370ff5d2230cd091
assets/fa-regular-400-DgEfZSYE.woff2,1726206215331,fb31b3f693b818441c452f39a682d8ad81967e3474b6a2266ddb885177cd98d1
assets/fa-regular-400-Bf3rG5Nx.ttf,1726206215334,61e9221981e1ad8a27ea29b9aed2d9d1615450c3f99b3a6175c2ef238fa67eb4
assets/fa-brands-400-O7nZalfM.woff2,1726206215331,5446f4c298ee6694ba92500f91741668e1ff9c8c08cb018a4f414fb663acae9e
assets/fa-solid-900-DOQJEhcS.woff2,1726206215334,7a9c4ef0dd299dfd976428e4cb9c86fc13ebd4eed7604f035476e69af3f56b9d
assets/fa-brands-400-Dur5g48u.ttf,1726206215334,8a036bca81921a2f3fc90c860fde21f0e8066cdf3507cf9999dd2a47cac380b1
assets/index-DJ_jKQnL.css,1726206215333,975c65ec679ed0e5f7d7955347d934f9e0c9de341190f1ada3adbfde4c8736a8
assets/index-BAYcpFy6.js,1726206215335,93ef0b5b97e8c5913cc5d2d53e285821f08b434426ab40660036f8651467be64
assets/fa-solid-900-BV3CbEM2.ttf,1726206215332,af1fb4d30a49d562da382fdc49f386e2c8075bbbe26a5ecbbcdcf8994aaa423d
2 changes: 1 addition & 1 deletion src/components/cart/MovieDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ const toggleComments = () => {
{{ movieStore.movie?.description }}
</p>
<div class="space-y-4 md:space-y-6">
<div class="space-y-4 md:space-y-2">
<div
v-for="season in movieStore.movie?.seasons"
:key="season.seasonNumber"
Expand Down
166 changes: 108 additions & 58 deletions src/components/chat/ChatComponent.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
<script setup>
import { ref, onMounted, watch } from 'vue';
import { ref, onMounted, watch, nextTick, onBeforeUnmount } from 'vue';
import { getAuth } from 'firebase/auth';
import { ref as dbRef, set, onValue, remove } from 'firebase/database';
import { ref as dbRef, set, onValue, remove, update } from 'firebase/database';
import { useRouter } from 'vue-router';
import { database } from '@/firebase';
import EmojiPicker from '@/components/chat/EmojiPicker.vue';
import IconSend from '@/components/icons/IconSend.vue';
const auth = getAuth();
const router = useRouter();
const currentUserId = ref(null);
const messagesRef = dbRef(database, 'messages');
const typingStatusRef = dbRef(database, 'typingStatus');
const newMessage = ref('');
const messages = ref([]);
const emojisVisible = ref(false);
Expand All @@ -21,17 +23,20 @@ const replyingTo = ref(null);
const messagesContainer = ref(null);
const displayedMessages = ref([]);
const maxDisplayedMessages = ref(10);
const isMobile = ref(window.innerWidth < 768);
const typingUsers = ref([]);
const loadNewMessages = () => {
const allMessages = messages.value;
displayedMessages.value = allMessages.slice(-maxDisplayedMessages.value);
scrollToBottom();
};
const scrollToBottom = () => {
if (messagesContainer.value) {
messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight;
}
nextTick(() => {
if (messagesContainer.value) {
messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight;
}
});
};
const confirmClearChat = () => {
Expand Down Expand Up @@ -80,37 +85,46 @@ onMounted(() => {
timestamp: parseInt(key),
}));
loadNewMessages();
scrollToBottom();
}
});
scrollToBottom();
onValue(typingStatusRef, (snapshot) => {
const data = snapshot.val() || {};
typingUsers.value = Object.values(data).filter(
(user) => user.id !== currentUserId.value
);
});
});
watch(messages, () => {
loadNewMessages();
scrollToBottom();
});
const sendMessage = () => {
const sendMessage = async () => {
if (newMessage.value.trim()) {
const timestamp = Date.now();
set(dbRef(database, 'messages/' + timestamp), {
text: newMessage.value,
timestamp: timestamp,
user: currentUser.value,
replyTo: replyingTo.value,
})
.then(() => {
newMessage.value = '';
replyingTo.value = null;
})
.catch((error) => {
console.error('Error sending message:', error);
try {
const timestamp = Date.now();
await set(dbRef(database, 'messages/' + timestamp), {
text: newMessage.value,
timestamp: timestamp,
user: currentUser.value,
replyTo: replyingTo.value,
});
newMessage.value = '';
replyingTo.value = null;
scrollToBottom();
} catch (error) {
console.error('Error sending message:', error);
}
}
stopTyping();
};
const handleKeyPress = (event) => {
if (event.key === 'Enter') {
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault();
sendMessage();
}
};
Expand All @@ -133,96 +147,132 @@ const formatDate = (timestamp) => {
const date = new Date(timestamp);
return date.toLocaleString();
};
const startTyping = async () => {
await update(dbRef(database, `typingStatus/${currentUserId.value}`), {
id: currentUserId.value,
name: currentUser.value.name,
});
};
const stopTyping = async () => {
await remove(dbRef(database, `typingStatus/${currentUserId.value}`));
};
watch(newMessage, (newVal) => {
if (newVal) {
startTyping();
} else {
stopTyping();
}
});
window.addEventListener('resize', () => {
isMobile.value = window.innerWidth < 768;
});
onBeforeUnmount(() => {
stopTyping();
});
</script>

<template>
<div class="messages-container flex flex-col h-full p-4 bg-gray-100 relative">
<div class="relative flex flex-col h-screen p-4 bg-gray-900 text-white">
<button
@click="confirmClearChat"
class="bg-red-600 text-white p-2 rounded-lg mb-4 shadow-lg hover:bg-red-700 transition-colors"
class="bg-red-600 text-white p-2 rounded-lg mb-4 shadow-md hover:bg-red-700 transition-colors w-full md:w-auto"
>
Очистити чат
</button>
<div
ref="messagesContainer"
class="flex-1 overflow-y-auto p-2 bg-white border border-gray-300 rounded-lg shadow-lg max-h-[calc(100vh-200px)]"
class="flex-1 overflow-y-auto p-2 bg-gray-800 border border-gray-700 rounded-lg shadow-lg"
>
<div
v-for="message in displayedMessages"
:key="message.timestamp"
class="flex items-start mb-4"
:class="[
'flex items-start mb-4',
message.user.id === currentUserId.value
? 'justify-end'
: 'justify-start',
]"
>
<img
:src="message.user?.avatar || 'https://via.placeholder.com/40'"
alt="avatar"
class="w-12 h-12 rounded-full mr-4 border-2 border-gray-300"
class="w-10 h-10 rounded-full mr-4 border-2 border-gray-700"
/>
<div
:class="[
'flex flex-col p-4 rounded-lg max-w-[70%] shadow-md relative',
message.replyTo
? 'bg-yellow-100 border-l-4 border-yellow-500'
: 'bg-gray-50',
'flex flex-col p-4 rounded-lg max-w-[75%] relative',
message.user.id === currentUserId.value
? 'bg-blue-600'
: 'bg-gray-700',
message.replyTo ? 'border-l-4 border-yellow-500' : '',
]"
>
<div class="flex items-center mb-2">
<strong class="text-gray-800 text-lg">{{
message.user?.name || 'Anonymous'
}}</strong>
<strong class="text-gray-100 text-lg">
{{ message.user?.name || 'Anonymous' }}
</strong>
</div>
<p class="text-gray-700 whitespace-pre-wrap">
<span v-if="message.replyTo" class="text-gray-600 italic block mb-2"
>Відповідає на {{ message.replyTo.user.name }}: "{{
message.replyTo.text
}}"</span
<p class="text-gray-300 whitespace-pre-wrap">
<span
v-if="message.replyTo"
class="text-gray-400 italic block mb-2"
>
Відповідає на {{ message.replyTo.user.name }}: "{{
message.replyTo.text
}}"
</span>
{{ message.text }}
</p>
<div class="flex items-center mt-2 text-gray-500 text-sm">
<span>{{ formatDate(message.timestamp) }}</span>
<button
v-if="!message.replyTo"
@click="replyMessage(message)"
class="ml-4 text-blue-500 hover:underline"
class="ml-4 text-blue-400 hover:underline"
>
Відповісти
</button>
</div>
</div>
</div>
<div v-if="typingUsers.length" class="text-gray-400 italic mb-4">
<span v-for="(user, index) in typingUsers" :key="user.id">
{{ user.name }}{{ index < typingUsers.length - 1 ? ', ' : '' }}
</span>
друкує...
</div>
</div>
<div v-if="emojisVisible" class="absolute bottom-16 right-4 z-20">
<EmojiPicker @emoji-select="handleEmojiSelect" />
</div>
<div class="flex items-center mt-4">
<div
class="flex items-center justify-between border-t border-gray-700 bg-gray-800 p-2"
>
<button
@click="toggleEmojiPicker"
class="bg-gray-700 p-2 rounded-lg text-gray-300 mr-2 hover:bg-gray-600"
>
😊
</button>
<input
id="message-input"
type="text"
v-model="newMessage"
@keypress="handleKeyPress"
placeholder="Введіть повідомлення..."
class="flex-1 border border-gray-300 p-2 rounded-l-lg bg-white text-gray-800 placeholder-gray-500 shadow-sm"
class="flex-1 border border-gray-700 bg-gray-900 p-2 rounded-lg text-white"
/>
<button
@click="toggleEmojiPicker"
class="bg-gray-200 p-2 rounded-r-lg text-gray-600"
>
😊
</button>
<button
@click="sendMessage"
class="bg-red-600 text-white p-2 rounded-lg ml-2 shadow-lg hover:bg-red-700 transition-colors"
class="bg-blue-600 text-white p-2 rounded-lg ml-2 flex-shrink-0 hover:bg-blue-500"
>
Надіслати
<IconSend />
</button>
</div>
</div>
</template>

<style scoped>
.messages-container {
max-height: 500px;
height: 100vh;
overflow-y: auto;
}
</style>
26 changes: 26 additions & 0 deletions src/components/icons/IconSend.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
style="isolation: isolate"
viewBox="0 0 64 64"
id="Send"
>
<defs>
<clipPath id="a">
<rect
width="64"
height="64"
fill="#ffffff"
class="color000000 svgShape"
></rect>
</clipPath>
</defs>
<g clip-path="url(#a)" fill="#ffffff" class="color000000 svgShape">
<path
d=" M 8.216 36.338 L 26.885 32.604 C 28.552 32.271 28.552 31.729 26.885 31.396 L 8.216 27.662 C 7.104 27.44 6.021 26.356 5.799 25.245 L 2.065 6.576 C 1.731 4.908 2.714 4.133 4.259 4.846 L 61.228 31.139 C 62.257 31.614 62.257 32.386 61.228 32.861 L 4.259 59.154 C 2.714 59.867 1.731 59.092 2.065 57.424 L 5.799 38.755 C 6.021 37.644 7.104 36.56 8.216 36.338 Z "
fill="#ffffff"
class="color000000 svgShape"
></path>
</g>
</svg>
</template>

0 comments on commit c11185d

Please sign in to comment.