improved mobile layout
This commit is contained in:
@@ -42,11 +42,12 @@ footer {
|
||||
|
||||
@media (max-width: 720px) {
|
||||
#content {
|
||||
padding: 1.2rem;
|
||||
padding: 12px;
|
||||
padding-top: 30px;
|
||||
}
|
||||
|
||||
footer {
|
||||
padding-bottom: 16px;
|
||||
padding-bottom: 56px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { initAuth, logout } from '../stores/auth.ts'
|
||||
import { initAuth, logout } from '../store.ts'
|
||||
import { API } from '../main.ts'
|
||||
import router from '../router'
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { apiFetch } from './client'
|
||||
import type { Friend, FriendRequest } from '../types/api'
|
||||
import type { Friend, FriendRequest } from '../api'
|
||||
|
||||
export function fetchFriends() {
|
||||
return apiFetch<Friend[]>('/friends')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { apiFetch } from './client'
|
||||
import type { Message } from '../types/api'
|
||||
import type { Message } from '../api'
|
||||
|
||||
export function fetchMessages(roomUuid: string) {
|
||||
return apiFetch<Message[]>(`/messages/${roomUuid}`)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { apiFetch } from './client'
|
||||
import type { Room } from '../types/api'
|
||||
import type { Room } from '../api'
|
||||
|
||||
export function fetchRooms(userUuid: string) {
|
||||
return apiFetch<Room[]>(`/rooms/${userUuid}`)
|
||||
|
||||
26
src/base.css
26
src/base.css
@@ -150,3 +150,29 @@ i:hover {
|
||||
.btn {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
i:hover {
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
button:hover, .button:hover {
|
||||
background: var(--accent-hover);
|
||||
}
|
||||
|
||||
input[type="checkbox"]:hover {
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
filter: brightness(1.2);
|
||||
}
|
||||
}
|
||||
|
||||
button:active, .nav-item:active {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
* {
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, watch, nextTick } from "vue";
|
||||
import { fetchMessages, sendMessage, getWsToken } from "../api/messages";
|
||||
import type { Message } from "../types/api";
|
||||
import type { Message } from "../types";
|
||||
import { API_WS } from '../main.ts'
|
||||
import MessageList from "./MessageList.vue";
|
||||
import MessageInput from "./MessageInput.vue";
|
||||
@@ -128,6 +128,8 @@ onUnmounted(() => {
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
font-size: 1.2rem;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.empty-state i {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { createRoom } from '../api/rooms'
|
||||
import type { Room } from '../types/api'
|
||||
import type { Room } from '../types'
|
||||
|
||||
const name = ref('')
|
||||
const global = ref(false)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import type { Message } from '../types/api'
|
||||
import type { Message } from '../api'
|
||||
defineProps<{ messages: Message[] }>()
|
||||
</script>
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router'
|
||||
import { logout as authLogout } from '../stores/auth.ts'
|
||||
import { logout as authLogout } from '../store.ts'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
@@ -38,6 +38,10 @@ function logout() {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 100vh;
|
||||
z-index: 50;
|
||||
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
@@ -67,16 +71,18 @@ function logout() {
|
||||
transform 0.15s ease;
|
||||
}
|
||||
|
||||
.nav-item:hover {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
@media (hover: hover) {
|
||||
.nav-item:hover {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
|
||||
.nav-item:not(.router-link-active):hover {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
.nav-item:not(.router-link-active):hover i {
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.nav-item:not(.router-link-active):hover i {
|
||||
color: var(--text);
|
||||
.nav-item.logout:hover i {
|
||||
color: rgba(255, 80, 80, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
.router-link-active i {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<div class="scroll-area">
|
||||
<router-link v-for="room in rooms" :key="room.uuid" :to="`/rooms/${room.uuid}`" class="btn room-item"
|
||||
:class="{ active: route.params.uuid === room.uuid }">
|
||||
:class="{ active: route.params.uuid === room.uuid }" @click="emit('select-room')">
|
||||
<div class="room-info">
|
||||
<span class="room-name">{{ room.name }}</span>
|
||||
<span class="room-owner">by {{ room.owner_name }}</span>
|
||||
@@ -22,15 +22,17 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { initAuth } from '../stores/auth.ts';
|
||||
import { initAuth } from '../store.ts';
|
||||
import { fetchRooms } from '../api/rooms';
|
||||
import type { Room } from '../types/api';
|
||||
import type { Room } from '../types';
|
||||
import CreateRoomModal from './CreateRoomModal.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const showCreate = ref(false);
|
||||
const rooms = ref<Room[]>([]);
|
||||
|
||||
const emit = defineEmits(['select-room']);
|
||||
|
||||
onMounted(async () => {
|
||||
const auth = await initAuth();
|
||||
rooms.value = await fetchRooms(auth.uuid!);
|
||||
@@ -42,6 +44,8 @@ onMounted(async () => {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.rooms-header {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createApp } from 'vue'
|
||||
import router from './router'
|
||||
import router from './router.ts'
|
||||
import App from './App.vue'
|
||||
import { validateToken } from './stores/auth.ts'
|
||||
import { validateToken } from './store.ts'
|
||||
|
||||
import './base.css'
|
||||
|
||||
@@ -15,5 +15,5 @@ async function init() {
|
||||
|
||||
init()
|
||||
|
||||
export const API = 'http://127.0.0.1:8080'
|
||||
export const API_WS = 'ws://127.0.0.1:8080/ws'
|
||||
export const API = 'http://192.168.1.183:8081'
|
||||
export const API_WS = 'ws://192.168.1.183:8081/ws'
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<aside class="sidebar" :class="{ 'is-open': isSidebarOpen }">
|
||||
<div class="sidebar-content">
|
||||
<RoomList />
|
||||
<RoomList @select-room="handleRoomSelection" />
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
@@ -24,8 +24,13 @@ import RoomList from "../components/RoomList.vue";
|
||||
import ChatWindow from "../components/ChatWindow.vue";
|
||||
|
||||
defineProps<{ uuid: string }>();
|
||||
|
||||
const isSidebarOpen = ref(true);
|
||||
|
||||
const handleRoomSelection = () => {
|
||||
if (window.innerWidth <= 720) {
|
||||
isSidebarOpen.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -64,6 +69,7 @@ const isSidebarOpen = ref(true);
|
||||
|
||||
.chat-window-container {
|
||||
flex: 1;
|
||||
padding-left: 38px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
@@ -124,8 +130,8 @@ const isSidebarOpen = ref(true);
|
||||
z-index: 15;
|
||||
}
|
||||
|
||||
.chat-window-container {
|
||||
padding-top: 50px;
|
||||
.menu-toggle:hover i {
|
||||
color: var(--muted);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { fetchFriends, fetchFriendRequests, acceptFriendRequest, sendFriendRequest } from '../api/friends'
|
||||
import type { Friend, FriendRequest } from '../types/api'
|
||||
import type { Friend, FriendRequest } from '../api'
|
||||
|
||||
const friends = ref<Friend[]>([])
|
||||
const requests = ref<FriendRequest[]>([])
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { login } from '../stores/auth.ts'
|
||||
import { login } from '../store.ts'
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const email = ref("");
|
||||
|
||||
52
src/router.ts
Normal file
52
src/router.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import { initAuth, getLastRoom, setLastRoom } from './store'
|
||||
|
||||
import LoginPage from './pages/LoginPage.vue'
|
||||
import ChatPage from './pages/ChatPage.vue'
|
||||
import FriendListPage from './pages/FriendListPage.vue'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes: [
|
||||
{ path: '/', name: 'root', component: { render: () => null } },
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
component: LoginPage,
|
||||
meta: { hideNavbar: true }
|
||||
},
|
||||
{
|
||||
path: '/rooms/:uuid',
|
||||
name: 'chat',
|
||||
component: ChatPage,
|
||||
props: true
|
||||
},
|
||||
{ path: '/friendlist', component: FriendListPage }
|
||||
],
|
||||
})
|
||||
|
||||
router.beforeEach(async (to) => {
|
||||
if (to.path === '/login') return true
|
||||
|
||||
const auth = await initAuth()
|
||||
if (!auth.isAuthenticated) {
|
||||
return '/login'
|
||||
}
|
||||
|
||||
// Handle the redirect from "/" to the last room
|
||||
if (to.path === '/') {
|
||||
const lastRoom = await getLastRoom()
|
||||
return `/rooms/${lastRoom || 'none'}`
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
// Save the room UUID to storage after every successful navigation to a chat room
|
||||
router.afterEach((to) => {
|
||||
if (to.name === 'chat' && to.params.uuid) {
|
||||
setLastRoom(to.params.uuid as string)
|
||||
}
|
||||
})
|
||||
|
||||
export default router
|
||||
@@ -1,40 +0,0 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import { initAuth } from '../stores/auth.ts'
|
||||
|
||||
import LoginPage from '../pages/LoginPage.vue'
|
||||
import ChatPage from '../pages/ChatPage.vue'
|
||||
import FriendListPage from '../pages/FriendListPage.vue'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes: [
|
||||
{ path: '/', redirect: '/rooms/none' },
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
component: LoginPage,
|
||||
meta: { hideNavbar: true }
|
||||
},
|
||||
{
|
||||
path: '/rooms/:uuid',
|
||||
name: 'chat',
|
||||
component: ChatPage,
|
||||
props: true
|
||||
},
|
||||
{ path: '/friendlist', component: FriendListPage }
|
||||
],
|
||||
})
|
||||
|
||||
router.beforeEach(async (to) => {
|
||||
if (to.path === '/login') return true
|
||||
|
||||
const auth = await initAuth()
|
||||
|
||||
if (!auth.isAuthenticated) {
|
||||
return '/login'
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
export default router
|
||||
@@ -1,6 +1,6 @@
|
||||
import { load, Store } from '@tauri-apps/plugin-store'
|
||||
import type { LoginResponse } from '../types/api'
|
||||
import { apiFetch } from '../api/client'
|
||||
import type { LoginResponse } from './types'
|
||||
import { apiFetch } from './api/client'
|
||||
|
||||
let store: Store | null = null
|
||||
|
||||
@@ -11,6 +11,20 @@ async function getStore() {
|
||||
return store
|
||||
}
|
||||
|
||||
export async function setLastRoom(uuid: string) {
|
||||
if (!uuid || uuid === 'none') return
|
||||
|
||||
const s = await getStore()
|
||||
await s.set('last_room_uuid', uuid)
|
||||
await s.save()
|
||||
}
|
||||
|
||||
export async function getLastRoom(): Promise<string | null> {
|
||||
const s = await getStore()
|
||||
const lastRoom = await s.get<string>('last_room_uuid')
|
||||
return lastRoom ?? null
|
||||
}
|
||||
|
||||
export async function initAuth() {
|
||||
const s = await getStore()
|
||||
const token = await s.get<string>('token')
|
||||
Reference in New Issue
Block a user