Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a875f84a5 | ||
|
|
a6e74ec808 | ||
|
|
2717137270 | ||
|
|
58ae55e061 | ||
|
|
eebb6906c6 | ||
|
|
0179d614b9 | ||
|
|
2794adaa21 | ||
|
|
37266ab8d4 | ||
|
|
74f5761e06 | ||
|
|
546234eacf | ||
|
|
7a822e8eaf | ||
|
|
b07f92853d | ||
|
|
3bb74eb672 | ||
|
|
8cf0f84ddc | ||
|
|
006b43a590 | ||
|
|
120b0c2647 | ||
|
|
a3702a900c | ||
|
|
ede81d551c | ||
|
|
7c19abb6ae | ||
|
|
8f93dde120 | ||
|
|
ca7b82dab5 | ||
|
|
fce8df9e84 | ||
|
|
1597f99eef | ||
|
|
9ad48ff310 | ||
|
|
af0ec78eae | ||
|
|
5f95df147f | ||
|
|
98cd855349 | ||
|
|
1aa14f9fd5 | ||
|
|
39cdd5e6c1 | ||
|
|
9bbefbcea8 | ||
|
|
9593e7c041 | ||
|
|
82aa2abfbd | ||
|
|
58c7165ce5 | ||
|
|
5bcf0eda0b | ||
|
|
0bfb140dc7 | ||
|
|
7b63799f0b | ||
|
|
a97c42323c | ||
|
|
fc0ed5ac2c | ||
|
|
69994d9856 | ||
|
|
843687ff2b | ||
|
|
7416aa37e0 | ||
|
|
ce83000e0c | ||
|
|
359e8ed63e | ||
|
|
3b9df94cf6 | ||
|
|
634f451595 | ||
|
|
00d7945f70 | ||
|
|
60d6a287e5 | ||
|
|
d5981b7475 | ||
|
|
523722bae6 | ||
|
|
71e6a58fbd | ||
|
|
a7e90e4ae5 | ||
|
|
654ab8b618 | ||
|
|
7a201808e3 | ||
|
|
77e4e444d4 | ||
|
|
bf1685a382 | ||
|
|
0ac43946b1 | ||
|
|
15ff7a20db | ||
|
|
1dd3f09fed | ||
|
|
87bdbc440a | ||
|
|
62b5a9db72 | ||
|
|
39156af48a | ||
|
|
3f40d3da29 | ||
|
|
a7a49f7441 | ||
|
|
e026e777f6 | ||
|
|
09a809c102 | ||
|
|
29ef37e237 | ||
|
|
713cb24508 | ||
|
|
589101c292 | ||
|
|
f807565cb2 | ||
|
|
6626d35920 | ||
|
|
5c76729a6b | ||
|
|
c260b8b231 | ||
|
|
971147dcb8 |
@@ -0,0 +1,15 @@
|
||||
# Contributing
|
||||
|
||||
As you might have gathered, this Gitea instance does not allow registrations. If you want to have an account on this instance in order to contribute to this project, you must contact me for an account. I am available on:
|
||||
|
||||
- XMPP: `x@sunglocto.net`
|
||||
- Matrix: `@x:sunglocto.net`
|
||||
- E-Mail: sunglocto <img width=16px height=16px src="https://sunglocto.net/icons/email.gif"/> protonmail.com
|
||||
|
||||
For actually submitting your patches, you can just fork this repo and submit a pull request. I currently have no contribution policy that you have to follow however if you just use common sense you should be good.
|
||||
|
||||
## AI contributions
|
||||
|
||||
Obviously vibe-coded or fully AI-generated contributions will be immediately rejected. This is in order to ensure the stability of the project and decrease the chance of significant bugs or vulnerabilities.
|
||||
|
||||
AI contributions that you have reviewed, edited and **ensured that they compile, run and work successfully** are acceptable. AI is a tool, and like any other tools, can be used for good if used in moderation.
|
||||
@@ -1,3 +1,11 @@
|
||||
# lambda
|
||||
|
||||
an XMPP client
|
||||
|
||||
Experimental XMPP client written in Go, with the GTK4 UI toolkit and the Fluux IO XMPP libraries.
|
||||
|
||||
icons are from Psi+ ([https://github.com/psi-im](https://github.com/psi-im))
|
||||
|
||||
additional icons are by Mark James's Silk Icon Set [https://github.com/markjames/famfamfam-silk-icons](https://github.com/markjames/famfamfam-silk-icons) and the [Haiku project](https://www.haiku-os.org/)
|
||||
|
||||
pepper & carrot default avatar is from [Pepper&Carrot](https://peppercarrot.com) - created by David Revoy, licensed under Creative Commons Attribution 4.0. Change made: cropped and made black & white
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
# TODO
|
||||
- XEP-0393: Message Styling 0%
|
||||
- XEP-0402: PEP Native Bookmarks 0%
|
||||
- XEP-0066: Out of Band Data partial
|
||||
- XEP-0461: Message Replies partial
|
||||
- XEP-0461: Message Replies 0%
|
||||
- XEP-0444: Message Reactions partial
|
||||
|
||||
|
||||
@@ -0,0 +1,306 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"github.com/diamondburned/gotk4/pkg/gdk/v4"
|
||||
"github.com/diamondburned/gotk4/pkg/gdkpixbuf/v2"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// If you want to change the default profile picture used when Identicons are disabled, you can change the below line to an image in the repository and recompile Lambda.
|
||||
|
||||
//go:embed debug_psi.png
|
||||
var defaultAvatarBytes []byte
|
||||
|
||||
//go:embed failed_load.png
|
||||
var failedBytes []byte
|
||||
|
||||
//go:embed assets/owner.png
|
||||
var ownerMedalBytes []byte
|
||||
|
||||
//go:embed assets/admin.png
|
||||
var adminMedalBytes []byte
|
||||
|
||||
//go:embed assets/member.png
|
||||
var memberMedalBytes []byte
|
||||
|
||||
//go:embed assets/noaffiliation.png
|
||||
var noneMedalBytes []byte
|
||||
|
||||
//go:embed assets/outcast.png
|
||||
var outcastMedalBytes []byte
|
||||
|
||||
//go:embed assets/cancel.png
|
||||
var cancelBytes []byte
|
||||
|
||||
//go:embed assets/status_away.png
|
||||
var sABytes []byte
|
||||
|
||||
//go:embed assets/status_busy.png
|
||||
var sBBytes []byte
|
||||
|
||||
//go:embed assets/status_chatty.png
|
||||
var sCBytes []byte
|
||||
|
||||
//go:embed assets/status_online.png
|
||||
var sOBytes []byte
|
||||
|
||||
//go:embed assets/status_xa.png
|
||||
var xaBytes []byte
|
||||
|
||||
//go:embed assets/tag.png
|
||||
var tagBytes []byte
|
||||
|
||||
//go:embed assets/lambda-disabled.png
|
||||
var logoDisabledBytes []byte
|
||||
|
||||
//go:embed assets/icon.png
|
||||
var logoBytes []byte
|
||||
|
||||
//go:embed assets/group.png
|
||||
var groupBytes []byte
|
||||
|
||||
//go:embed assets/door_in.png
|
||||
var doorInBytes []byte
|
||||
|
||||
//go:embed assets/door_out.png
|
||||
var doorOutBytes []byte
|
||||
|
||||
//go:embed assets/large_group.png
|
||||
var largeGroupBytes []byte
|
||||
|
||||
//go:embed assets/world.png
|
||||
var worldBytes []byte
|
||||
|
||||
//go:embed assets/disconnect.png
|
||||
var disconnectBytes []byte
|
||||
|
||||
//go:embed assets/chart_bar.png
|
||||
var barBytes []byte
|
||||
|
||||
//go:embed assets/chart_bar_laggy.png
|
||||
var barLaggyBytes []byte
|
||||
|
||||
//go:embed assets/ok.png
|
||||
var okBytes []byte
|
||||
|
||||
//go:embed assets/hourglass.png
|
||||
var hourglassBytes []byte
|
||||
|
||||
//go:embed assets/connect_tls.png
|
||||
var connectBytes []byte
|
||||
|
||||
//go:embed assets/comment.png
|
||||
var commentBytes []byte
|
||||
|
||||
//go:embed assets/information.png
|
||||
var informationBytes []byte
|
||||
|
||||
//go:embed assets/car.png
|
||||
var carBytes []byte
|
||||
|
||||
//go:embed assets/car_high.png
|
||||
var carHighBytes []byte
|
||||
|
||||
// muc icons
|
||||
|
||||
//go:embed assets/muc_open.png
|
||||
var mucOpenBytes []byte
|
||||
|
||||
//go:embed assets/muc_membersonly.png
|
||||
var mucMembersOnlyBytes []byte
|
||||
|
||||
//go:embed assets/muc_passwordprotected.png
|
||||
var mucPasswordProtectedBytes []byte
|
||||
|
||||
//go:embed assets/muc_unsecured.png
|
||||
var mucUnsecuredBytes []byte
|
||||
|
||||
//go:embed assets/muc_hidden.png
|
||||
var mucHiddenBytes []byte
|
||||
|
||||
//go:embed assets/muc_public.png
|
||||
var mucPublicBytes []byte
|
||||
|
||||
//go:embed assets/muc_unmoderated.png
|
||||
var mucUnmoderatedBytes []byte
|
||||
|
||||
//go:embed assets/muc_moderated.png
|
||||
var mucModeratedBytes []byte
|
||||
|
||||
//go:embed assets/muc_nonanonymous.png
|
||||
var mucNonAnonymousBytes []byte
|
||||
|
||||
//go:embed assets/muc_semianonymous.png
|
||||
var mucSemiAnonymousBytes []byte
|
||||
|
||||
//go:embed assets/muc_persistent.png
|
||||
var mucPersistentBytes []byte
|
||||
|
||||
//go:embed assets/muc_temporary.png
|
||||
var mucTemporaryBytes []byte
|
||||
|
||||
//go:embed assets/moderate.png
|
||||
var moderateBytes []byte
|
||||
|
||||
//go:embed assets/jabber.png
|
||||
var jabberBytes []byte
|
||||
|
||||
//go:embed assets/fail.png
|
||||
var failBytes []byte
|
||||
|
||||
//go:embed assets/please_wait.gif
|
||||
var pleaseWaitBytes []byte
|
||||
|
||||
//go:embed assets/update.png
|
||||
var updateBytes []byte
|
||||
|
||||
//go:embed assets/solar.png
|
||||
var solarBytes []byte
|
||||
|
||||
//go:embed assets/timeline_marker.png
|
||||
var timelineMarkerBytes []byte
|
||||
|
||||
//go:embed assets/cap.png
|
||||
var capIconBytes []byte
|
||||
|
||||
// client icons
|
||||
|
||||
//go:embed assets/client_icons/monocles.png
|
||||
var monoclesCapBytes []byte
|
||||
|
||||
//go:embed assets/client_icons/gajim.png
|
||||
var gajimCapBytes []byte
|
||||
|
||||
//go:embed assets/client_icons/conversations.png
|
||||
var conversationsCapBytes []byte
|
||||
|
||||
//go:embed assets/client_icons/dino.png
|
||||
var dinoCapBytes []byte
|
||||
|
||||
//go:embed assets/client_icons/blabber.png
|
||||
var blabberCapBytes []byte
|
||||
|
||||
//go:embed assets/client_icons/profanity.png
|
||||
var profanityCapBytes []byte
|
||||
|
||||
//go:embed assets/client_icons/poezio.png
|
||||
var poezioCapBytes []byte
|
||||
|
||||
//go:embed assets/client_icons/mcabber.png
|
||||
var mcabberCapBytes []byte
|
||||
|
||||
//go:embed assets/client_icons/converse.png
|
||||
var converseJSCapBytes []byte
|
||||
|
||||
//go:embed assets/client_icons/pidgin.png
|
||||
var pidginCapBytes []byte
|
||||
|
||||
//go:embed assets/client_icons/anotherim.png
|
||||
var anotherImCapBytes []byte
|
||||
|
||||
//go:embed assets/client_icons/movim.png
|
||||
var movimCapBytes []byte
|
||||
|
||||
//go:embed assets/client_icons/yaxim.png
|
||||
var yaximCapBytes []byte
|
||||
|
||||
//go:embed assets/client_icons/psi.png
|
||||
var psiCapBytes []byte
|
||||
|
||||
var clientAssets sync.Map
|
||||
|
||||
func loadAsset(key string, data []byte) {
|
||||
loader := gdkpixbuf.NewPixbufLoader()
|
||||
loader.Write(data)
|
||||
loader.Close()
|
||||
clientAssets.Store(key, gdk.NewTextureForPixbuf(loader.Pixbuf()))
|
||||
}
|
||||
|
||||
func clientAssetsLoad(key string) gdk.Paintabler {
|
||||
dat, ok := clientAssets.Load(key)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
paintable, ok := dat.(gdk.Paintabler)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return paintable
|
||||
}
|
||||
|
||||
func init() {
|
||||
for key, data := range map[string][]byte{
|
||||
"logo": logoBytes,
|
||||
"DefaultAvatar": defaultAvatarBytes,
|
||||
"FailedAvatar": failedBytes,
|
||||
"owner": ownerMedalBytes,
|
||||
"admin": adminMedalBytes,
|
||||
"member": memberMedalBytes,
|
||||
"none": noneMedalBytes,
|
||||
"outcast": outcastMedalBytes,
|
||||
"cancel": cancelBytes,
|
||||
"tag": tagBytes,
|
||||
"disabled_logo": logoDisabledBytes,
|
||||
"group": groupBytes,
|
||||
"door_in": doorInBytes,
|
||||
"door_out": doorOutBytes,
|
||||
"large_group": largeGroupBytes,
|
||||
"world": worldBytes,
|
||||
"disconnect": disconnectBytes,
|
||||
"chart_bar": barBytes,
|
||||
"chart_bar_laggy": barLaggyBytes,
|
||||
"ok": okBytes,
|
||||
"hourglass": hourglassBytes,
|
||||
"connect": connectBytes,
|
||||
"comment": commentBytes,
|
||||
"information": informationBytes,
|
||||
"status_away": sABytes,
|
||||
"status_dnd": sBBytes,
|
||||
"status_chat": sCBytes,
|
||||
"status_xa": xaBytes,
|
||||
"status_": sOBytes,
|
||||
"car": carBytes,
|
||||
"car_high": carHighBytes,
|
||||
"muc_open": mucOpenBytes,
|
||||
"muc_membersonly": mucMembersOnlyBytes,
|
||||
"muc_passwordprotected": mucPasswordProtectedBytes,
|
||||
"muc_unsecured": mucUnsecuredBytes,
|
||||
"muc_hidden": mucHiddenBytes,
|
||||
"muc_public": mucPublicBytes,
|
||||
"muc_unmoderated": mucUnmoderatedBytes,
|
||||
"muc_moderated": mucModeratedBytes,
|
||||
"muc_nonanonymous": mucNonAnonymousBytes,
|
||||
"muc_semianonymous": mucSemiAnonymousBytes,
|
||||
"muc_persistent": mucPersistentBytes,
|
||||
"muc_temporary": mucTemporaryBytes,
|
||||
"moderate": moderateBytes,
|
||||
"jabber": jabberBytes,
|
||||
"fail": failBytes,
|
||||
"please_wait": pleaseWaitBytes,
|
||||
"update": updateBytes,
|
||||
"solar": solarBytes,
|
||||
"timeline_marker": timelineMarkerBytes,
|
||||
"cap": capIconBytes,
|
||||
|
||||
"https://monocles.eu": monoclesCapBytes,
|
||||
"https://gajim.org": gajimCapBytes,
|
||||
"http://conversations.im": conversationsCapBytes,
|
||||
"https://dino.im": dinoCapBytes,
|
||||
"http://blabber.im": blabberCapBytes,
|
||||
"http://profanity-im.github.io": profanityCapBytes,
|
||||
"https://poez.io": poezioCapBytes,
|
||||
"http://mcabber.com/caps": mcabberCapBytes,
|
||||
"https://conversejs.org": converseJSCapBytes,
|
||||
"http://pidgin.im/": pidginCapBytes,
|
||||
"https://dev.narayana.im/narayana/Conversations-Classic": anotherImCapBytes,
|
||||
"https://movim.eu/": movimCapBytes,
|
||||
"https://yaxim.org/": yaximCapBytes,
|
||||
"https://psi-im.org": psiCapBytes,
|
||||
} {
|
||||
loadAsset(key, data)
|
||||
}
|
||||
|
||||
}
|
||||
|
After Width: | Height: | Size: 762 B |
|
After Width: | Height: | Size: 226 KiB |
|
After Width: | Height: | Size: 607 B |
|
After Width: | Height: | Size: 287 B |
|
After Width: | Height: | Size: 671 B |
|
After Width: | Height: | Size: 656 B |
|
After Width: | Height: | Size: 456 B |
|
After Width: | Height: | Size: 641 B |
|
After Width: | Height: | Size: 541 B |
|
After Width: | Height: | Size: 434 B |
@@ -0,0 +1 @@
|
||||
All client assets are owned by their respective owners
|
||||
|
After Width: | Height: | Size: 783 B |
|
After Width: | Height: | Size: 196 B |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 246 B |
|
After Width: | Height: | Size: 551 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 362 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 588 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 263 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 413 B |
|
After Width: | Height: | Size: 721 B |
|
After Width: | Height: | Size: 796 B |
|
After Width: | Height: | Size: 693 B |
|
After Width: | Height: | Size: 643 B |
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 753 B |
|
After Width: | Height: | Size: 744 B |
|
After Width: | Height: | Size: 264 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 778 B |
|
After Width: | Height: | Size: 730 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 747 B |
|
After Width: | Height: | Size: 793 B |
|
After Width: | Height: | Size: 593 B |
|
After Width: | Height: | Size: 935 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 970 B |
|
After Width: | Height: | Size: 508 B |
|
After Width: | Height: | Size: 612 B |
|
After Width: | Height: | Size: 806 B |
|
After Width: | Height: | Size: 622 B |
|
After Width: | Height: | Size: 924 B |
|
After Width: | Height: | Size: 882 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 903 B |
|
After Width: | Height: | Size: 460 B |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 543 B |
|
After Width: | Height: | Size: 794 B |
|
After Width: | Height: | Size: 751 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 722 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 910 B |
|
After Width: | Height: | Size: 327 B |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 362 B |
|
After Width: | Height: | Size: 923 B |
@@ -5,6 +5,7 @@ package main
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/diamondburned/gotk4/pkg/gdk/v4"
|
||||
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
||||
@@ -13,10 +14,17 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// global or app-level map/cache
|
||||
var textureCache = make(map[string]gdk.Paintabler)
|
||||
// var textureCache = make(map[string]gdk.Paintabler)
|
||||
var textureCache sync.Map
|
||||
|
||||
// Invalid images, if an image/avatar cannot be loaded on the system (e.g: incompatible format) it's put here
|
||||
// var invalidImages = make(map[string]bool)
|
||||
|
||||
var invalidImages sync.Map
|
||||
|
||||
func ensureCache() (string, error) {
|
||||
cachePath := configdir.LocalCache("lambda-im")
|
||||
@@ -28,49 +36,70 @@ func ensureCache() (string, error) {
|
||||
return cachePath, nil
|
||||
}
|
||||
|
||||
func getTexture(path string) gdk.Paintabler {
|
||||
if tex, exists := textureCache[path]; exists {
|
||||
return tex
|
||||
func getTexture(path string) (gdk.Paintabler, error) {
|
||||
tex, exists := textureCache.Load(path)
|
||||
if exists {
|
||||
return tex.(gdk.Paintabler), nil
|
||||
}
|
||||
|
||||
tex, err := gdk.NewTextureFromFilename(path) // load once
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil, err
|
||||
}
|
||||
textureCache[path] = tex
|
||||
return tex
|
||||
textureCache.Store(path, tex)
|
||||
t, ok := tex.(gdk.Paintabler)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid type")
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func newPictureFromPath(path string) *gtk.Picture {
|
||||
tex := getTexture(path)
|
||||
func newPictureFromPath(path string) (*gtk.Picture, error) {
|
||||
tex, err := getTexture(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
img := gtk.NewPictureForPaintable(tex)
|
||||
return img
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func newImageFromPath(path string) *gtk.Image {
|
||||
tex := getTexture(path)
|
||||
img := gtk.NewImageFromPaintable(tex)
|
||||
return img
|
||||
func newImageFromPath(path string) (*gtk.Image, error) {
|
||||
tex, err := getTexture(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return gtk.NewImageFromPaintable(tex), nil
|
||||
}
|
||||
|
||||
func newPictureFromWeb(url string) *gtk.Picture {
|
||||
func setImageFromPath(path string, i *gtk.Image) error {
|
||||
tex, err := getTexture(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
i.SetFromPaintable(tex)
|
||||
return nil
|
||||
}
|
||||
|
||||
func newPictureFromWeb(url string) (*gtk.Picture, error) {
|
||||
pa, _ := ensureCache()
|
||||
// step 1: get a sha256 sum of the URL
|
||||
sum := fmt.Sprintf("%x", sha256.Sum256([]byte(url)))
|
||||
|
||||
p, ok := textureCache[sum]
|
||||
p, ok := textureCache.Load(sum)
|
||||
if ok {
|
||||
return gtk.NewPictureForPaintable(p)
|
||||
return gtk.NewPictureForPaintable(p.(gdk.Paintabler)), nil
|
||||
}
|
||||
|
||||
// step 2: download it
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return nil
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fullpath := filepath.Join(pa, sum)
|
||||
@@ -78,31 +107,31 @@ func newPictureFromWeb(url string) *gtk.Picture {
|
||||
// step 3: save it
|
||||
err = os.WriteFile(fullpath, b, 0644)
|
||||
if err != nil {
|
||||
return nil
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newPictureFromPath(fullpath)
|
||||
}
|
||||
|
||||
func newImageFromWeb(url string) *gtk.Image {
|
||||
func newImageFromWeb(url string) (*gtk.Image, error) {
|
||||
pa, _ := ensureCache()
|
||||
// step 1: get a sha256 sum of the URL
|
||||
sum := fmt.Sprintf("%x", sha256.Sum256([]byte(url)))
|
||||
|
||||
p, ok := textureCache[sum]
|
||||
p, ok := textureCache.Load(sum)
|
||||
if ok {
|
||||
return gtk.NewImageFromPaintable(p)
|
||||
return gtk.NewImageFromPaintable(p.(gdk.Paintabler)), nil
|
||||
}
|
||||
|
||||
// step 2: download it
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return nil
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fullpath := filepath.Join(pa, sum)
|
||||
@@ -110,7 +139,7 @@ func newImageFromWeb(url string) *gtk.Image {
|
||||
// step 3: save it
|
||||
err = os.WriteFile(fullpath, b, 0644)
|
||||
if err != nil {
|
||||
return nil
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newImageFromPath(fullpath)
|
||||
|
||||
|
After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 91 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
@@ -0,0 +1,38 @@
|
||||
name: Run tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.go'
|
||||
- 'go.*'
|
||||
- .github/workflows/test.yaml
|
||||
|
||||
push:
|
||||
paths:
|
||||
- '**.go'
|
||||
- 'go.*'
|
||||
- .github/workflows/test.yaml
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Go 1.25
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: 1.25
|
||||
id: go
|
||||
- uses: actions/checkout@v1
|
||||
- name: Run tests
|
||||
run: |
|
||||
go test ./... -v -race -coverprofile cover.out -covermode=atomic
|
||||
- name: Convert coverage to lcov
|
||||
uses: jandelgado/gcov2lcov-action@v1
|
||||
with:
|
||||
infile: cover.out
|
||||
outfile: coverage.lcov
|
||||
- name: Coveralls
|
||||
uses: coverallsapp/github-action@v1
|
||||
with:
|
||||
github-token: ${{ secrets.github_token }}
|
||||
path-to-lcov: coverage.lcov
|
||||
@@ -0,0 +1,36 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
coverage.out
|
||||
coverage.txt
|
||||
|
||||
.idea/
|
||||
*.iml
|
||||
.DS_Store
|
||||
|
||||
# Do not commit codeship key
|
||||
codeship.aes
|
||||
codeship.env
|
||||
|
||||
priv/
|
||||
@@ -0,0 +1,58 @@
|
||||
# Fluux XMPP Changelog
|
||||
|
||||
## v0.5.0
|
||||
|
||||
### Changes
|
||||
|
||||
- Added support for XEP-0198 (Stream management)
|
||||
- Added message queue : when using "SendX" methods on a client, messages are also stored in a queue. When requesting
|
||||
acks from the server, sent messages will be discarded, and unsent ones will be sent again. (see https://xmpp.org/extensions/xep-0198.html#acking)
|
||||
- Added support for stanza_errors (see https://xmpp.org/rfcs/rfc3920.html#def C.2. Stream error namespace and https://xmpp.org/rfcs/rfc6120.html#schemas-streamerror)
|
||||
- Added separate hooks for connection and reconnection on the client. One can now specify different actions to get triggered on client connect
|
||||
and reconnect, at client init time.
|
||||
- Client state update is now thread safe
|
||||
- Changed the Config struct to use pointer semantics
|
||||
- Tests
|
||||
- Refactoring, including removing some Fprintf statements in favor of Marshal + Write and using structs from the library
|
||||
instead of strings
|
||||
|
||||
## v0.4.0
|
||||
|
||||
### Changes
|
||||
|
||||
- Added support for XEP-0060 (PubSub)
|
||||
(no support for 6.5.4 Returning Some Items yet as it needs XEP-0059, Result Sets)
|
||||
- Added support for XEP-0050 (Commands)
|
||||
- Added support for XEP-0004 (Forms)
|
||||
- Updated the client example with a TUI
|
||||
- Make keepalive interval configurable #134
|
||||
- Fix updating of EventManager.CurrentState #136
|
||||
- Added callbacks for error management in Component and Client. Users must now provide a callback function when using NewClient/Component.
|
||||
- Moved JID from xmpp package to stanza package
|
||||
|
||||
## v0.3.0
|
||||
|
||||
### Changes
|
||||
|
||||
- Update requirements to go1.13
|
||||
- Add a websocket transport
|
||||
- Add Client.SendIQ method
|
||||
- Add IQ result routes to the Router
|
||||
- Fix SIGSEGV in xmpp_component (#126)
|
||||
- Add tests for Component and code style fixes
|
||||
|
||||
## v0.2.0
|
||||
|
||||
### Changes
|
||||
|
||||
- XMPP Over Websocket support
|
||||
- Add support for getting IQ responses to client IQ queries (synchronously or asynchronously, passing an handler
|
||||
function).
|
||||
- Implement X-OAUTH2 authentication method. You can read more details here:
|
||||
[Understanding ejabberd OAuth Support & Roadmap: Step 4](https://blog.process-one.net/understanding-ejabberd-oauth-support-roadmap/)
|
||||
- Fix issues in the stanza builder when trying to add text inside and XMPP node.
|
||||
- Fix issues with unescaped % characters in XMPP payload.
|
||||
|
||||
### Code migration guide
|
||||
|
||||
TODO
|
||||