- 
          
- 
                Notifications
    You must be signed in to change notification settings 
- Fork 6.2k
Live removal of issue comments using htmx websocket #28958
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 58 commits
2b247dd
              5005245
              7cf50ef
              4947abc
              3b35637
              e85f85b
              d77073b
              17f4faf
              948f1bd
              1d6a34d
              337456a
              e4beba8
              21bcb1b
              c2ca7d8
              fb49e6b
              4c03093
              7173bea
              bc2a352
              26ab35c
              b2f4aae
              afcdd78
              21485f8
              7cda3aa
              d9c4554
              ea06a25
              9ace44c
              78b94ff
              c595916
              b805631
              e9ded26
              b5f61df
              7de741c
              bdde78a
              3d2ec73
              4f97ad2
              72bb32f
              c1512d3
              201fd83
              39421d6
              634834b
              671a8df
              94bcdba
              77f89b7
              1602afd
              f2f97a2
              4d75e28
              7f3435f
              cf23cd6
              bb4443d
              372faaa
              d86c1dc
              70f2fee
              154f61c
              8919106
              1328b2b
              0aa3d5e
              40fc078
              c6abd32
              e9502e0
              b8dab69
              088454f
              43ad9f5
              7cb8e2f
              602a42a
              6af641f
              f83d6fe
              0262846
              aed4810
              a2e1c14
              85cf27e
              abcc609
              597fac4
              63f4e6c
              7daafae
              edb0f9c
              f2001a2
              cbfb0bd
              56d11fc
              877eb69
              d34e36b
              e6da1b1
              5ceae12
              7efef85
              97544d8
              File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| // Copyright 2023 The Gitea Authors. All rights reserved. | ||
| // SPDX-License-Identifier: MIT | ||
|  | ||
| package websocket | ||
|  | ||
| import ( | ||
| "code.gitea.io/gitea/modules/web" | ||
| "code.gitea.io/gitea/services/context" | ||
| "code.gitea.io/gitea/services/websocket" | ||
| ) | ||
|  | ||
| func Init(r *web.Route) { | ||
| m := websocket.Init() | ||
|  | ||
| r.Any("/-/ws", func(ctx *context.Context) { | ||
| err := m.HandleRequest(ctx.Resp, ctx.Req) | ||
| if err != nil { | ||
| ctx.ServerError("HandleRequest", err) | ||
| return | ||
| } | ||
| }) | ||
| } | ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| // Copyright 2024 The Gitea Authors. All rights reserved. | ||
| // SPDX-License-Identifier: MIT | ||
|  | ||
| package pubsub | ||
|  | ||
| import ( | ||
| "context" | ||
| "sync" | ||
| ) | ||
|  | ||
| type Memory struct { | ||
| sync.Mutex | ||
|  | ||
| topics map[string]map[*Subscriber]struct{} | ||
| } | ||
|  | ||
| // New creates an in-memory publisher. | ||
| func NewMemory() Broker { | ||
| return &Memory{ | ||
| topics: make(map[string]map[*Subscriber]struct{}), | ||
| } | ||
| } | ||
|  | ||
| func (p *Memory) Publish(_ context.Context, message Message) { | ||
| p.Lock() | ||
|  | ||
| topic, ok := p.topics[message.Topic] | ||
| if !ok { | ||
| p.Unlock() | ||
| return | ||
| } | ||
|  | ||
| for s := range topic { | ||
| go (*s)(message) | ||
| } | ||
| p.Unlock() | ||
| } | ||
|  | ||
| func (p *Memory) Subscribe(c context.Context, topic string, subscriber Subscriber) { | ||
| // Subscribe | ||
| p.Lock() | ||
| _, ok := p.topics[topic] | ||
| if !ok { | ||
| p.topics[topic] = make(map[*Subscriber]struct{}) | ||
| } | ||
| p.topics[topic][&subscriber] = struct{}{} | ||
| p.Unlock() | ||
|  | ||
| // Wait for context to be done | ||
| <-c.Done() | ||
|  | ||
| // Unsubscribe | ||
| p.Lock() | ||
| delete(p.topics[topic], &subscriber) | ||
| if len(p.topics[topic]) == 0 { | ||
| delete(p.topics, topic) | ||
| } | ||
| p.Unlock() | ||
| } | 
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| // Copyright 2024 The Gitea Authors. All rights reserved. | ||
| // SPDX-License-Identifier: MIT | ||
|  | ||
| package pubsub | ||
|  | ||
| import ( | ||
| "context" | ||
| "sync" | ||
| "testing" | ||
| "time" | ||
|  | ||
| "github.com/stretchr/testify/assert" | ||
| ) | ||
|  | ||
| func TestPubsub(t *testing.T) { | ||
| var ( | ||
| wg sync.WaitGroup | ||
|  | ||
| testMessage = Message{ | ||
| Data: []byte("test"), | ||
| Topic: "hello-world", | ||
| } | ||
| ) | ||
|  | ||
| ctx, cancel := context.WithCancelCause( | ||
| context.Background(), | ||
| ) | ||
|  | ||
| broker := NewMemory() | ||
| go func() { | ||
| broker.Subscribe(ctx, "hello-world", func(message Message) { assert.Equal(t, testMessage, message); wg.Done() }) | ||
| }() | ||
| go func() { | ||
| broker.Subscribe(ctx, "hello-world", func(_ Message) { wg.Done() }) | ||
| }() | ||
|  | ||
| // Wait a bit for the subscriptions to be registered | ||
| <-time.After(100 * time.Millisecond) | ||
|  | ||
| wg.Add(2) | ||
| go func() { | ||
| broker.Publish(ctx, testMessage) | ||
| }() | ||
|  | ||
| wg.Wait() | ||
| cancel(nil) | ||
| } | 
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| // Copyright 2024 The Gitea Authors. All rights reserved. | ||
| // SPDX-License-Identifier: MIT | ||
|  | ||
| package pubsub | ||
|  | ||
| import "context" | ||
|  | ||
| // Message defines a published message. | ||
| type Message struct { | ||
| // Data is the actual data in the entry. | ||
| Data []byte `json:"data"` | ||
|  | ||
| // Topic is the topic of the message. | ||
| Topic string `json:"topic"` | ||
| } | ||
|  | ||
| // Subscriber receives published messages. | ||
| type Subscriber func(Message) | ||
|  | ||
| type Broker interface { | ||
| Publish(c context.Context, message Message) | ||
| Subscribe(c context.Context, topic string, subscriber Subscriber) | ||
| } | 
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| // Copyright 2024 The Gitea Authors. All rights reserved. | ||
| // SPDX-License-Identifier: MIT | ||
|  | ||
| package websocket | ||
|  | ||
| import ( | ||
| "context" | ||
| "encoding/json" | ||
| "fmt" | ||
|  | ||
| issues_model "code.gitea.io/gitea/models/issues" | ||
| user_model "code.gitea.io/gitea/models/user" | ||
| "code.gitea.io/gitea/services/pubsub" | ||
| ) | ||
|  | ||
| func (n *websocketNotifier) DeleteComment(ctx context.Context, doer *user_model.User, c *issues_model.Comment) { | ||
| d, err := json.Marshal(c) | ||
| if err != nil { | ||
| return | ||
| } | ||
|  | ||
| n.pubsub.Publish(ctx, pubsub.Message{ | ||
|          | ||
| Data: d, | ||
| Topic: fmt.Sprintf("repo:%s/%s", c.RefRepo.OwnerName, c.RefRepo.Name), | ||
| }) | ||
| } | ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| // Copyright 2024 The Gitea Authors. All rights reserved. | ||
| // SPDX-License-Identifier: MIT | ||
|  | ||
| package websocket | ||
|  | ||
| import ( | ||
| "code.gitea.io/gitea/modules/templates" | ||
| notify_service "code.gitea.io/gitea/services/notify" | ||
| "code.gitea.io/gitea/services/pubsub" | ||
|  | ||
| "github.com/olahol/melody" | ||
| ) | ||
|  | ||
| type websocketNotifier struct { | ||
| notify_service.NullNotifier | ||
| m *melody.Melody | ||
| rnd *templates.HTMLRender | ||
| pubsub pubsub.Broker | ||
| } | ||
|  | ||
| // NewNotifier create a new webhooksNotifier notifier | ||
| func newNotifier(m *melody.Melody) notify_service.Notifier { | ||
| return &websocketNotifier{ | ||
| m: m, | ||
| rnd: templates.HTMLRenderer(), | ||
| } | ||
| } | ||
|  | ||
| // htmxAddElementEnd = "<div hx-swap-oob=\"beforebegin:%s\">%s</div>" | ||
| // htmxUpdateElement = "<div hx-swap-oob=\"outerHTML:%s\">%s</div>" | ||
|  | ||
| var htmxRemoveElement = "<div hx-swap-oob=\"delete:%s\"></div>" | 
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| // Copyright 2024 The Gitea Authors. All rights reserved. | ||
| // SPDX-License-Identifier: MIT | ||
|  | ||
| package websocket | ||
|  | ||
| import ( | ||
| "fmt" | ||
| "net/url" | ||
|  | ||
| user_model "code.gitea.io/gitea/models/user" | ||
|  | ||
| "github.com/olahol/melody" | ||
| ) | ||
|  | ||
| type sessionData struct { | ||
| user *user_model.User | ||
| onURL string | ||
| } | ||
|  | ||
| func (d *sessionData) isOnURL(_u1 string) bool { | ||
| if d.onURL == "" { | ||
| return true | ||
| } | ||
|  | ||
| u1, _ := url.Parse(d.onURL) | ||
| u2, _ := url.Parse(_u1) | ||
| return u1.Path == u2.Path | ||
| } | ||
|  | ||
| func getSessionData(s *melody.Session) (*sessionData, error) { | ||
| _data, ok := s.Get("data") | ||
| if !ok { | ||
| return nil, fmt.Errorf("no session data") | ||
| } | ||
|  | ||
| data, ok := _data.(*sessionData) | ||
| if !ok { | ||
| return nil, fmt.Errorf("invalid session data") | ||
| } | ||
|  | ||
| return data, nil | ||
| } | 
Uh oh!
There was an error while loading. Please reload this page.