@adi@twtxt.net I think it is, and one benefit they have is that you can add third-party repositories to the F-Droid app as you discover them. So, for instance, if you know of a developer who pushes builds to an F-Droid compatible repository, you can add that to your F-Droid app and start tracking updates like you would for any other app in there. Canāt do that with Google Play!
F-Droid tends to focus on open source applications that can be built in a reproducible way, which limits the inventory (though of course tends to mean the apps are safer and donāt spy on you). There are non-free apps in there as well but they come with warnings so youāre informed about what you might be sacrificing by using them.
That said if you have a favorite app you get through Google Play, thereās a decent chance it wonāt be in F-Droid. Many ābig corporateā apps arenāt, and vendor-specific apps tend not to be either. But for most of the major functions you might want, like email clients, calendar apps, weather apps, etc etc, there are very good substitutes now in F-Droid. Youāre definitely making a trade-off though.
What I did was go through the apps I had installed on my last phone, found as many substitutes in F-Droid as I could, started using those instead to see how they worked, and bit by bit replaced as much as I could from Google Play with a comparable app from F-Droid. I still have a few apps (mostly vendor-specific things that donāt have substitutes) that come from Google Play but Iām aiming to be rid of those before I need to replace this phone.
@movq@www.uninformativ.de @mckinley@twtxt.net I believe the resurgence in availability of municipal WiFi is largely driven by the surveillance capabilities it offers. Every person who has WiFi enabled on their phone can be tracked throughout the city as their phones ping various base stations; a lot of folks arenāt aware of just how much information can be slurped out of a phone that isnāt locked down just from its WiFi pings. I know this happens in Toronto, and I was familiar with a startup in Massachusetts that based its business model on this very concept. I can only assume itās widespread in the US if not throughout the Western world.
@prologic@twtxt.net It was super useful if you needed to do the sorts of things it did. Iām pretty sad.
At its core was Sage, a computational mathematics system, and their own version of Jupyter notebooks. So, you could do all kinds of different math stuff in a notebook environment and share that with people. But on top of that, there was a chat system, a collaborative editing system, a course management system (so if you were teaching a class using it you could keep track of students, assignments, grades, that sort of thing), and a bunch of other stuff I never used. It all ran in a linux container with python/conda as a base, so you could also drop to a terminal, install stuff in the container, and run X11 applications in the same environment. I never taught a class with it but I used to use it semi-regularly to experiment with ideas.
@lyse@lyse.isobeef.org I knew from the get go it was going to be an annoying thing to track down, which is was, but that made it take even longer because I avoided trying.
My desktop computer developed a really annoying vibration-induced buzzing sound a few months ago after I added some hard drives to it. It was one of these where itād be more or less quiet, and then all of a sudden a buzzing would start. If you tapped the case, it often made the buzzing stop.
One by one I went through my components, and the day before yesterday I finally identified the guilty party, one particular HDD. Currently I have the case open and a piece of cardboard jammed under the drive in its tray. The computer has not buzzed since I did that, so it looks to me like securing that drive better will finally end this madness-inducing sound.
Wild that it takes so long to track down something like this and figure out what to do about it.
@lyse@lyse.isobeef.org that could definitely be a track in an ambient song, no question whatsoever.
The exhaust is amazingly soothing to look at, even though itād vaporize your entire being in milliseconds if you were anywhere near it.
(cont.)
Just to give some context on some of the components around the code structure.. I wrote this up around an earlier version of aggregate code. This generic bit simplifies things by removing the need of the Crud functions for each aggregate.
Domain ObjectsA domain object can be used as an aggregate by adding the event.AggregateRoot struct and finish implementing event.Aggregate. The AggregateRoot implements logic for adding events after they are either Raised by a command or Appended by the eventstore Load or service ApplyFn methods. It also tracks the uncommitted events that are saved using the eventstore Save method.
type User struct {
Identity string ```json:"identity"`
CreatedAt time.Time
event.AggregateRoot
}
// StreamID for the aggregate when stored or loaded from ES.
func (a *User) StreamID() string {
return "user-" + a.Identity
}
// ApplyEvent to the aggregate state.
func (a *User) ApplyEvent(lis ...event.Event) {
for _, e := range lis {
switch e := e.(type) {
case *UserCreated:
a.Identity = e.Identity
a.CreatedAt = e.EventMeta().CreatedDate
/* ... */
}
}
}
Events
Events are applied to the aggregate. They are defined by adding the event.Meta and implementing the getter/setters for event.Event
type UserCreated struct {
eventMeta event.Meta
Identity string
}
func (c *UserCreated) EventMeta() (m event.Meta) {
if c != nil {
m = c.eventMeta
}
return m
}
func (c *UserCreated) SetEventMeta(m event.Meta) {
if c != nil {
c.eventMeta = m
}
}
Reading Events from EventStore
With a domain object that implements the event.Aggregate the event store client can load events and apply them using the Load(ctx, agg) method.
// GetUser populates an user from event store.
func (rw *User) GetUser(ctx context.Context, userID string) (*domain.User, error) {
user := &domain.User{Identity: userID}
err := rw.es.Load(ctx, user)
if err != nil {
if err != nil {
if errors.Is(err, eventstore.ErrStreamNotFound) {
return user, ErrNotFound
}
return user, err
}
return nil, err
}
return user, err
}
OnX Commands
An OnX command will validate the state of the domain object can have the command performed on it. If it can be applied it raises the event using event.Raise() Otherwise it returns an error.
// OnCreate raises an UserCreated event to create the user.
// Note: The handler will check that the user does not already exsist.
func (a *User) OnCreate(identity string) error {
event.Raise(a, &UserCreated{Identity: identity})
return nil
}
// OnScored will attempt to score a task.
// If the task is not in a Created state it will fail.
func (a *Task) OnScored(taskID string, score int64, attributes Attributes) error {
if a.State != TaskStateCreated {
return fmt.Errorf("task expected created, got %s", a.State)
}
event.Raise(a, &TaskScored{TaskID: taskID, Attributes: attributes, Score: score})
return nil
}
Crud Operations for OnX Commands
The following functions in the aggregate service can be used to perform creation and updating of aggregates. The Update function will ensure the aggregate exists, where the Create is intended for non-existent aggregates. These can probably be combined into one function.
// Create is used when the stream does not yet exist.
func (rw *User) Create(
ctx context.Context,
identity string,
fn func(*domain.User) error,
) (*domain.User, error) {
session, err := rw.GetUser(ctx, identity)
if err != nil && !errors.Is(err, ErrNotFound) {
return nil, err
}
if err = fn(session); err != nil {
return nil, err
}
_, err = rw.es.Save(ctx, session)
return session, err
}
// Update is used when the stream already exists.
func (rw *User) Update(
ctx context.Context,
identity string,
fn func(*domain.User) error,
) (*domain.User, error) {
session, err := rw.GetUser(ctx, identity)
if err != nil {
return nil, err
}
if err = fn(session); err != nil {
return nil, err
}
_, err = rw.es.Save(ctx, session)
return session, err
}
Itāll track a bunch of finger(1) endpoints and let you see whatās new. Very early draft. Not actually a social network, more an anti-social network for ā80s CompSci transplants. :-)
trying some day planning on paper, quantizing all tasks into pomodoros. feels good. my goal is to make software only if/when Iāll feel Iāll need it and have a pretty decent idea of WHAT I need #tracking #selfimprovement #time
yarnd, the mobile app nor API support this anyway...
@movq@www.uninformativ.de i believe the delete of any twt was a tech limitation with retwt parser not knowing where in the file a twt came from. lextwt tracks the bytes in file where a twt was read from. which could be used to delete a twt from file.. in theory.
Convocations comprises five volumesāMeditations, Lamentations, Revelations, Celebrations and Incantationsāthe project is a two-and-a-half-hour, 49-track reflection on a year of anxiety, uncertainty, isolation, and loss. https://music.sufjan.com/album/convocations
rapping in english is hard. i can do it, but its so much more work to grasp the flow. i can cover some aesop rock tracks and a struggle some, but then iām just flowing in spanish. maybe i should just rap in spanish lmao
@prologic@twtxt.net in theory shouldnāt need to let users add feeds.. if they get mentioned by a tracked feed they will get added automagically. on a pod it would just need to scan the twtxt feed to know about everyone.
@lyse@lyse.isobeef.org @prologic@twtxt.net very curious⦠i worked on a very similar track. i built a spider that will trace off any follows = comments and mentions from other users and came up with:
twters: 744
total: 52073
@prologic@twtxt.net pod should probably track revocation of device keys and delete the encryptedkeys that are paired with revoked keys
@lucidiot@tilde.town [re: abandoned ideas] Thanks for the inspiration! How do you keep track of projects now? Do you know about TaskWarrior?
Obsessed with this track right now https://junodreams.bandcamp.com/track/be-with-me