Skip to content

Commit

Permalink
login
Browse files Browse the repository at this point in the history
  • Loading branch information
xdzqyyds committed Dec 4, 2024
1 parent 12bbc5c commit 7c9ca60
Show file tree
Hide file tree
Showing 10 changed files with 325 additions and 123 deletions.
2 changes: 2 additions & 0 deletions server/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ func NewApi(rdb *redis.Client, secret string, live777Url string, live777Token st

r.Get("/login/userlist", handle.UserList)
r.Patch("/login/offline", handle.UpdateUserList)
r.Post("/login/invite", handle.Invite)
r.Patch("/login/invitee", handle.GetInvitation)
r.Post("/room/", handle.CreateRoom)
r.Get("/room/{roomId}", handle.ShowRoom)
//r.Patch("/room/{roomId}", handle.UpdateRoom)
Expand Down
79 changes: 79 additions & 0 deletions server/api/v1/invite.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package v1

import (
"encoding/json"
"fmt"
"log"
"net/http"
"woom/server/model"

"github.com/redis/go-redis/v9"
)

func (h *Handler) Invite(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()

// 解析请求体中的JSON数据
var inviteData struct {
MeetingId string `json:"meetingId"`
InviterId string `json:"inviterId"`
InviteeId string `json:"inviteeId"`
}

if err := json.NewDecoder(r.Body).Decode(&inviteData); err != nil {
http.Error(w, "Invalid request payload", http.StatusBadRequest)
return
}

inviteValue := fmt.Sprintf("%s %s", inviteData.MeetingId, inviteData.InviterId)

err := h.rdb.HSet(ctx, model.InvitationKey, inviteData.InviteeId, inviteValue).Err()
if err != nil {
log.Printf("Failed to save invitation for user %s: %v", inviteData.InviteeId, err)
http.Error(w, "Failed to store invitation", http.StatusInternalServerError)
return
}

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
response := map[string]string{
"success": "true",
"message": "Invitation sent successfully",
}
if err := json.NewEncoder(w).Encode(response); err != nil {
log.Printf("Failed to encode response: %v", err)
http.Error(w, "Failed to send response", http.StatusInternalServerError)
}
}

func (h *Handler) GetInvitation(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()

var reqBody struct {
InviteeId string `json:"inviteeId"`
}

if err := json.NewDecoder(r.Body).Decode(&reqBody); err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}

inviteeId := reqBody.InviteeId
value, err := h.rdb.HGet(ctx, model.InvitationKey, inviteeId).Result()

if err == redis.Nil {
return
} else if err != nil {
return
}

w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(map[string]string{"value": value}); err != nil {
http.Error(w, "Failed to encode response", http.StatusInternalServerError)
return
}

if err := h.rdb.HDel(ctx, model.InvitationKey, inviteeId).Err(); err != nil {
log.Printf("Failed to delete inviteeId %s from invitation: %v\n", inviteeId, err)
}
}
2 changes: 1 addition & 1 deletion server/model/redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
const (
UserStorageKey = "user_storage"
UserOnlineStatusKey = "user_online_status"
InvitationKey = "invitation"
)

func ClearRedis(rdb *redis.Client) {
Expand Down Expand Up @@ -43,7 +44,6 @@ func InitUserData(rdb *redis.Client) {
log.Printf("Failed to set user %s online status: %v\n", user, err)
}
}

}

func GenerateUsers() map[string]string {
Expand Down
74 changes: 37 additions & 37 deletions webapp/components/Invite.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,37 @@
import { useState } from 'react'
import { sendInvite } from '../lib/api'

interface InviteProps {
meetingId: string;
inviterId: string;
inviteeId: string;
}

export default function Invite({ meetingId, inviterId, inviteeId }: InviteProps) {
const [isInvited, setIsInvited] = useState<boolean>(false)

const handleInvite = async () => {
try {
const response = await sendInvite(meetingId, inviterId, inviteeId)
if (response.success) {
setIsInvited(true)
console.log('Invite sent successfully')
} else {
console.error('Failed to send invite')
}
} catch (error) {
console.error('Error sending invite:', error)
}
}

return (
<div>
<button
onClick={handleInvite}
className={`bg-blue-500 text-white p-2 rounded-md ${isInvited ? 'bg-green-500' : ''}`}
>
{isInvited ? 'Invited' : 'Invite'}
</button>
</div>
)
}
import { useState } from 'react'
import { sendInvite } from '../lib/api'

interface InviteProps {
meetingId: string;
inviterId: string;
inviteeId: string;
}

export default function Invite({ meetingId, inviterId, inviteeId }: InviteProps) {
const [isInvited, setIsInvited] = useState<boolean>(false)

const handleInvite = async () => {
try {
const response = await sendInvite(meetingId, inviterId, inviteeId)
if (response.success) {
setIsInvited(true)
console.log('Invite sent successfully')
} else {
console.error('Failed to send invite')
}
} catch (error) {
console.error('Error sending invite:', error)
}
}

return (
<div>
<button
onClick={handleInvite}
className={`bg-blue-500 text-white p-2.8 rounded-md ${isInvited ? 'bg-green-500' : ''}`}
>
{isInvited ? 'Invited' : 'Invite'}
</button>
</div>
)
}
182 changes: 100 additions & 82 deletions webapp/components/userlist.tsx
Original file line number Diff line number Diff line change
@@ -1,82 +1,100 @@
import { useEffect, useState } from 'react'
import { getUserOnlineStatus, updateUserStatus } from '../lib/api'
import { getStorage } from '../lib/storage'

export default function UserList() {
const [userStatus, setUserStatus] = useState<{ [userId: string]: string }>({})
const [isOpen, setIsOpen] = useState<boolean>(false)

const fetchUserStatus = async () => {
try {
const status = await getUserOnlineStatus()
setUserStatus(status)
} catch (error) {
console.error('Failed to fetch user status:', error)
}
}

useEffect(() => {
fetchUserStatus()
const interval = setInterval(fetchUserStatus, 5000)
return () => clearInterval(interval)
}, [])

useEffect(() => {
const cleanup = async () => {
const userId = getStorage()?.userId
updateUserStatus(userId, '0')
}

window.addEventListener('beforeunload', cleanup)
window.addEventListener('unload', cleanup)
return () => {
window.removeEventListener('beforeunload', cleanup)
window.removeEventListener('unload', cleanup)
}
}, [])

const sortedUserStatus = Object.keys(userStatus)
.sort((_, b) => (userStatus[b] === '1' ? 1 : -1))
.map((userId) => ({
userId,
status: userStatus[userId],
}))

return (
<div className="relative">
<button
onClick={() => setIsOpen(!isOpen)}
className="absolute bottom-4 right-4 bg-blue-500 text-white p-3 rounded-full shadow-lg"
>
{isOpen ? (
<span className="text-xl"></span>
) : (
<span className="text-xl"></span>
)}
</button>

{isOpen && (
<div className="absolute bottom-16 right-4 bg-white p-4 rounded-lg shadow-lg max-w-xs w-full max-h-[200px] overflow-y-auto">
<h3 className="font-bold mb-2">User Online Status</h3>
<ul className="space-y-2">
{sortedUserStatus.map(({ userId, status }) => (
<li key={userId} className="flex items-center justify-between space-x-1">
<span className="font-bold text-lg text-blue-600">{userId}</span>
<div className="flex items-center space-x-2">
{status === '1' ? (
<span className="text-green-500">✔️</span>
) : (
<span className="text-red-500"></span>
)}
<span className={status === 'true' ? 'text-green-500' : 'text-red-500'}>
{status === '1' ? 'Online' : 'Offline'}
</span>
</div>
</li>
))}
</ul>
</div>
)}
</div>
)
}
import { useEffect, useState } from 'react'
import { useAtom } from 'jotai'
import { getUserOnlineStatus, updateUserStatus } from '../lib/api'
import { getStorage } from '../lib/storage'
import { meetingIdAtom } from '../store/atom'
import Invite from './Invite'

export default function UserList() {
const [userStatus, setUserStatus] = useState<{ [userId: string]: string }>({})
const [isOpen, setIsOpen] = useState<boolean>(false)

const [meeting] = useAtom(meetingIdAtom)

const inviterId = getStorage()?.userId
const meetingId = getStorage()?.meeting

const fetchUserStatus = async () => {
try {
const status = await getUserOnlineStatus()
setUserStatus(status)
} catch (error) {
console.error('Failed to fetch user status:', error)
}
}

useEffect(() => {
fetchUserStatus()
const interval = setInterval(fetchUserStatus, 5000)
return () => clearInterval(interval)
}, [])

useEffect(() => {
const cleanup = async () => {
updateUserStatus(inviterId, '0')
}

window.addEventListener('beforeunload', cleanup)
window.addEventListener('unload', cleanup)
return () => {
window.removeEventListener('beforeunload', cleanup)
window.removeEventListener('unload', cleanup)
}
}, [])

const sortedUserStatus = Object.keys(userStatus)
.sort((_, b) => (userStatus[b] === '1' ? 1 : -1))
.map((userId) => ({
userId,
status: userStatus[userId],
}))

return (
<div className="relative">
<button
onClick={() => setIsOpen(!isOpen)}
className="absolute bottom-4 right-4 bg-blue-500 text-white p-3 rounded-full shadow-lg"
>
{isOpen ? (
<span className="text-xl"></span>
) : (
<span className="text-xl"></span>
)}
</button>

{isOpen && (
<div className="absolute bottom-16 right-4 bg-white p-4 rounded-lg shadow-lg max-w-xs w-full max-h-[200px] overflow-y-auto">
<h3 className="font-bold mb-2">User Online Status</h3>
<ul className="space-y-2">
{sortedUserStatus.map(({ userId, status }) => (
<li key={userId} className="flex items-center justify-between space-x-1">
<span className="font-bold text-lg text-blue-600">{userId}</span>

<div className="flex items-center space-x-2">
{status === '1' ? (
<span className="text-green-500">✔️</span>
) : (
<span className="text-red-500"></span>
)}
<span className={status === 'true' ? 'text-green-500' : 'text-red-500'}>
{status === '1' ? 'Online' : 'Offline'}
</span>
</div>

{status === '1' && meeting ? (
<Invite
meetingId={meetingId}
inviterId={inviterId}
inviteeId={userId}
/>
) : (
<span className="text-gray-500">Disabled</span>
)}
</li>
))}
</ul>
</div>
)}
</div>
)
}
Loading

0 comments on commit 7c9ca60

Please sign in to comment.