Skip to content

Commit c4e5aa7

Browse files
committed
feat: check whether user have closed the tab
1 parent 201a276 commit c4e5aa7

File tree

5 files changed

+74
-52
lines changed

5 files changed

+74
-52
lines changed

frontend/src/App.vue

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,23 @@ async function onUIOpened() {
4646
const session = params.get('session')
4747
if (!session)
4848
return
49-
await serviceStore.fetch('ui/opened', {
50-
method: 'POST',
51-
body: JSON.stringify({ session }),
52-
})
49+
pingActive()
50+
51+
async function pingActive() {
52+
try {
53+
const resp = await serviceStore.fetch('ui/active', {
54+
method: 'POST',
55+
body: JSON.stringify({ session }),
56+
})
57+
const data = await resp.json()
58+
if (data.continue !== false) {
59+
setTimeout(pingActive, 1000)
60+
}
61+
}
62+
catch {
63+
setTimeout(pingActive, 1000)
64+
}
65+
}
5366
}
5467
onUIOpened()
5568
</script>

frontend/src/stores/service.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,14 @@ export const useServiceStore = defineStore('service', () => {
2626
})
2727
let requireFindService = true
2828

29-
const givenServerPort = Number.parseInt(params.get('port') || '')
30-
if (givenServerPort > 0 && givenServerPort < 65535) {
31-
servicePort.value = givenServerPort
32-
requireFindService = false
33-
}
34-
3529
const initialLoad = new Promise<void>((resolve) => {
30+
const givenServerPort = Number.parseInt(params.get('port') || '')
31+
if (givenServerPort > 0 && givenServerPort < 65535) {
32+
servicePort.value = givenServerPort
33+
requireFindService = false
34+
resolve()
35+
}
36+
3637
useIntervalFn(
3738
async () => {
3839
if (requireFindService) {
@@ -58,7 +59,7 @@ export const useServiceStore = defineStore('service', () => {
5859

5960
useIntervalFn(() => {
6061
requireFindService = true
61-
}, 3000)
62+
}, 5000)
6263

6364
return {
6465
serverConnected,

service/logic/open.go

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,15 @@ import (
1313
"github.com/google/uuid"
1414
)
1515

16-
var openingUI = make(map[string](chan struct{}))
1716
var ServerPort = -1
1817

19-
func OpenUI(params url.Values, auth bool) error {
18+
var sessionActive = make(map[string]chan<- struct{})
19+
20+
func OpenUI(params url.Values, auth bool) (<-chan struct{}, func(), error) {
2021
if auth {
2122
allUsers, err := store.Users.List()
2223
if err != nil {
23-
return err
24+
return nil, nil, err
2425
}
2526

2627
var userName string
@@ -32,7 +33,7 @@ func OpenUI(params url.Values, auth bool) error {
3233

3334
token, err := GenerateJWT(userName)
3435
if err != nil {
35-
return err
36+
return nil, nil, err
3637
}
3738
params.Set("username", userName)
3839
params.Set("token", token)
@@ -42,28 +43,27 @@ func OpenUI(params url.Values, auth bool) error {
4243
params.Set("session", sessionId)
4344
params.Set("port", strconv.Itoa(ServerPort))
4445

45-
err := openBrowser.OpenBrowser(constants.UserName, constants.AppBaseUrl+"?"+params.Encode())
46-
if err != nil {
47-
return err
48-
}
46+
openBrowser.OpenBrowser(constants.UserName, constants.AppBaseUrl+"?"+params.Encode())
4947

5048
channel := make(chan struct{})
51-
openingUI[sessionId] = channel
52-
defer delete(openingUI, sessionId)
49+
sessionActive[sessionId] = channel
50+
cleanup := func() {
51+
delete(sessionActive, sessionId)
52+
}
5353

5454
select {
5555
case <-channel:
56-
return nil
56+
return channel, cleanup, nil
5757
case <-time.After(5 * time.Second):
58-
return fmt.Errorf("failed to open UI: timeout")
58+
cleanup()
59+
return nil, nil, fmt.Errorf("failed to open UI: timeout")
5960
}
6061
}
6162

62-
func OnUIOpened(sessionId string) {
63-
channel, ok := openingUI[sessionId]
64-
if !ok {
65-
return
63+
func OnUIActive(sessionId string) bool {
64+
channel, ok := sessionActive[sessionId]
65+
if ok {
66+
channel <- struct{}{}
6667
}
67-
channel <- struct{}{}
68-
close(channel)
68+
return ok
6969
}

service/server/action.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
func SetupActionAPI(router *gin.Engine) {
1414
router.GET("/", handleCheck)
1515
router.GET("/ui/open", handleOpenUI)
16-
router.POST("/ui/opened", handleUIOpened)
16+
router.POST("/ui/active", handleUIActive)
1717
}
1818

1919
func handleCheck(c *gin.Context) {
@@ -24,15 +24,18 @@ func handleCheck(c *gin.Context) {
2424
}
2525

2626
func handleOpenUI(c *gin.Context) {
27-
err := logic.OpenUI(url.Values{}, true)
27+
_, cleanup, err := logic.OpenUI(url.Values{}, true)
28+
if cleanup != nil {
29+
cleanup()
30+
}
2831

2932
if err != nil {
3033
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to open UI"})
3134
return
3235
}
3336
}
3437

35-
func handleUIOpened(c *gin.Context) {
38+
func handleUIActive(c *gin.Context) {
3639
var req struct {
3740
Session string `json:"session"`
3841
}
@@ -41,6 +44,7 @@ func handleUIOpened(c *gin.Context) {
4144
return
4245
}
4346

44-
logic.OnUIOpened(req.Session)
45-
c.Status(http.StatusOK)
47+
shouldContinue := logic.OnUIActive(req.Session)
48+
49+
c.JSON(http.StatusOK, gin.H{"continue": shouldContinue})
4650
}

service/server/app.go

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func SetupAppAPI(router gin.IRouter) {
2121
router.Group("/app").Use(RequireUserLogin()).DELETE("/clear", handleAppClear)
2222
}
2323

24-
var waitForGrant = make(map[string]chan bool)
24+
var waitForGrant = make(map[string]chan<- bool)
2525

2626
func handleAppRegister(c *gin.Context) {
2727
var req struct {
@@ -98,35 +98,39 @@ func handleAppRegister(c *gin.Context) {
9898

9999
channel := make(chan bool)
100100
waitForGrant[uid] = channel
101+
defer delete(waitForGrant, uid)
101102

102103
params := url.Values{
103104
"action": {"register"},
104105
"appId": {uid},
105106
"appName": {req.Name},
106107
"appDescription": {req.Description},
107108
}
108-
err := logic.OpenUI(params, true)
109-
110-
timeout := 10 * time.Minute
109+
uiActive, cleanup, err := logic.OpenUI(params, true)
110+
if cleanup != nil {
111+
defer cleanup()
112+
}
111113
if err != nil {
112-
timeout = 10 * time.Second
114+
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to open UI: %v", err)})
115+
return
113116
}
114117

115-
select {
116-
case result := <-channel:
117-
if result {
118-
granted()
119-
} else {
120-
c.JSON(http.StatusForbidden, gin.H{"error": "App registration denied"})
121-
}
122-
case <-time.After(timeout):
123-
if err != nil {
124-
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to open UI: %v", err)})
125-
} else {
126-
c.JSON(http.StatusRequestTimeout, gin.H{"error": "App registration timed out"})
118+
for {
119+
select {
120+
case result := <-channel:
121+
if result {
122+
granted()
123+
} else {
124+
c.JSON(http.StatusForbidden, gin.H{"error": "App registration denied"})
125+
}
126+
return
127+
case <-uiActive:
128+
continue
129+
case <-time.After(5 * time.Second):
130+
c.JSON(http.StatusRequestTimeout, gin.H{"error": "App registration timed out. User may have closed the tab"})
131+
return
127132
}
128133
}
129-
delete(waitForGrant, uid)
130134
}
131135

132136
func handleAppList(c *gin.Context) {

0 commit comments

Comments
 (0)