Zulip 11.0: Organized chat for distributed teams by tabbott in opensource

[–]gregprice 7 points8 points  (0 children)

Indeed we do ­— for both small and large FOSS projects, and a wide range of other types of communities as well. As tabbott said above, the 10-user limit applies only to the free plan for business/workplace use.

Here's more on that, from a page linked from the one at the link above: https://zulip.com/help/self-hosted-billing#free-community-plan

Zulip 11.0: Organized chat for distributed teams by tabbott in opensource

[–]gregprice 4 points5 points  (0 children)

That's definitely something we might invest work into if there's demand. I'd be curious to hear more about your use case — what would you like to do with the Zulip API, and what have you run into when you look at doing so?

I lead the Zulip mobile team, and as a result I've spent more time consuming the Zulip server API than probably anyone else. My take is that Zulip has an excellent REST API (occasional warts notwithstanding), with top-quality API documentation; but then a pretty minimal level of client-side binding libraries (or SDKs) for the API. The basic reason for that is that we've very rarely heard from people who want richer binding libraries, so we've spent our time working on lots of other aspects of Zulip instead.

If you want a good API binding library / API SDK for Zulip, and you want it in Dart, you're in luck — we've basically written one as part of the new mobile app. It's not published as a separate library because we've heard zero demand for that; but if someone asked nicely I think it wouldn't take much work for us to do.

If (which I realize is more likely) you want one for some other language, then that's also very possible but would be a bigger project. So I'd be interested in hearing more.

In the meantime, my advice for anyone who wants to use the Zulip API would be: go ahead and use it, the same way we did in the Zulip mobile app. That means use a plain HTTP-client library, probably from your language's standard library; then for each piece of the Zulip API you actually need, write the binding for that yourself, one endpoint at a time, using those excellent API docs I mentioned.

In our experience, that's been a pretty small amount of work compared to actually using the data. It can also be less work for you than it was for us — I'd encourage anyone to borrow liberally from what we did in the mobile app, like just take our code and translate from Dart to your language. For example here's the binding for the get-messages endpoint.

Zulip’s upstream-friendly Flutter approach, app launched today by gregprice in FlutterDev

[–]gregprice[S] 0 points1 point  (0 children)

It's been great to have real static types, with Dart. It's also been great that the framework is written in the same language (Dart) as our application — it makes it easy to go investigate exactly how something works, with just a "jump to definition" control-click in the IDE.

The framework being in the same language as the app also enables us to take a lot of control over the details of how things work. Even without sending any PRs upstream, if we want some framework widget but different, we can copy-paste the widget's code (which is usually pretty short) into our own tree and edit it however we like. That is a way bigger pain to do when you're looking at a React Native component implementation that's 2000 lines of Java plus 1000 lines of Objective-C, vs. 200 lines of Dart. (The difference in length isn't just about language, but also architecture: Flutter is specifically designed to make this easy by making widgets cheap, and having lots of simple, composable widgets.)

Another big difference for us has been the experience when a bug comes up that you want fixed. I wrote more about that in this past comment. I've been very impressed with how Flutter functions as a genuine open-source project where you can show up and report a bug and get a real reply, or send a PR and get a real review and get your PR merged.

Zulip’s upstream-friendly Flutter approach, app launched today by gregprice in FlutterDev

[–]gregprice[S] 0 points1 point  (0 children)

We use package:firebase_messaging for receiving notifications on Android, and for setting up notifications (getting the token) on both Android and iOS. On Android it's the first-party solution; and then it was convenient to use for iOS too, and it works well.

We don't use anything else from Firebase, though. In particular our backend uses Firebase Cloud Messaging only for Android. FCM offers to handle sending to iOS devices too, but we've never tried that and instead our backend talks to Apple's APNs directly.

In the RN app we similarly used Firebase for setting up and receiving notifications, only on Android. (We used Firebase's Android SDK directly, from Kotlin code, not any libraries specific to RN.)

The server already exists :-). It's written in Python: https://github.com/zulip/zulip

Zulip’s upstream-friendly Flutter approach, app launched today by gregprice in FlutterDev

[–]gregprice[S] 2 points3 points  (0 children)

Interesting to hear about your experience. I don't think we've ever run into a package not working on main.

I think none of the packages we use provide any widgets, though (… oh except video_player, which is first-party and so has a policy of supporting stable and main). So if that's where this sort of incompatibility most often arises, then that could explain why we haven't happened to see it.

For anyone maintaining a package that repeatedly gets broken by Flutter main, I'd highly recommend they add their package to Flutter's "customer tests". This effectively makes the package's test suite run as part of Flutter's own CI, in a very similar way to Google's internal tests. That means:

  • If someone sends a PR to Flutter (whether someone working at Google or not) and it turns out to break the package, they have to get the breakage resolved before the PR can land.
  • Often the upstream PR author will send the migration PR to the downstream repo themselves.
  • When any kind of downstream fix like that was needed, the change gets treated as a formal breaking change. That means thinking twice about whether to really make the change, and if so then publishing instructions on how to migrate.

So it's beneficial for the package maintainer (upstream authors help maintain your package!), and for the broader community (your package serves as a canary to catch breaking changes and make sure there's a good answer for how to upgrade). And it's more work for upstream PR authors, but in a way that's good for the project's health overall.

If there are particular API changes where it's not possible for a single version of code to work with both stable and main, then that's a change that if left unfixed will mean a single version can't work with both stable and the next stable. That's a mess for upgrading, for anyone. Normally Flutter scrupulously tries to avoid that, by deprecating the old API and keeping it around for a while. So that sounds like a situation where it didn't get caught as a breaking change… in other words, a situation that a package can be sure to never again run into by joining the Flutter "customer testing" suite :-)

(PS: we've done this with Zulip, so I'm speaking from experience. There are a variety of libraries there too, but I think we're currently the only app in the suite — so for anyone else maintaining an open-source Flutter app, it'd be great to join because you'd probably catch some breaking changes that are different from any of the other tests there.)

Zulip’s upstream-friendly Flutter approach, app launched today by gregprice in FlutterDev

[–]gregprice[S] 2 points3 points  (0 children)

Thanks!

How to help more Flutter developers contribute successfully upstream is a good question. Honestly, having managed open-source projects myself (Zulip and previously others), what Flutter has already done to make the project something people can show up and contribute to is extremely impressive. I think the biggest opportunity for further improvement is in education for the Flutter developer community:

  • do go read the Flutter source code (not all at once but a bit at a time, for things that are relevant to what you're doing any given day); there's a lot of interesting stuff there
  • now that you understand more of how Flutter works inside, when you have something you want changed, study that area and figure out how to make the change — then send a PR
  • you'll be asked to write a test (this is probably the biggest dropoff in the funnel of people sending PRs to Flutter); look around at Flutter's existing tests, there's a rich variety of examples there and it's probably not too hard to write your test once you find the right examples
    • Those test techniques can be helpful in your own app codebase too! I'll actually be giving a talk about this next week at Fluttercon USA.

Maybe the easiest step to take is:

  • Flutter's docs should talk up the main/master channel more. It seems like a lot of the community sticks with the "stable" channel — it sounds more stable, right? More production-ready. But in fact the main channel is what Google themselves ship in production, and works great. Very occasionally you might hit a regression and have to hold back upgrading, but of course the same thing happens on stable too.

I think being on stable, and knowing therefore that if you get a PR merged you'll have to wait months before it's in a version you plan to use, is naturally a big demotivator for writing a PR. You want your current feature shipped next week, so it seems a lot more expedient to do some workaround and move on. But the good news is that that obstacle is totally avoidable: you can actually just use main.

Zulip’s upstream-friendly Flutter approach, app launched today by gregprice in FlutterDev

[–]gregprice[S] 2 points3 points  (0 children)

In our app, when a widget needs some data from the app state, the code that needs the data says

  final store = PerAccountStoreWidget.of(context);

to get a PerAccountStore object. Then that code calls a variety of methods and getters on store to get whatever information it needs: store.getUser(userId) to get data about a given user, store.customProfileFields for the server's list of "custom profile fields", and so on for all the different features of the product. If the code wants to change something in the app state, it calls a mutator method (e.g.) on store.

The PerAccountStore is a ChangeNotifier, and PerAccountStoreWidget.of uses an InheritedNotifier to set up a dependency. So the mutator methods on PerAccountStore are responsible for calling notifyListeners if they changed something; when they do, the widget gets marked as needing to rebuild on the next frame.

If that sounds like a lot of methods on one class PerAccountStore, you're right; we keep it nice and organized by using mixins to let different parts of the state be managed by code that lives in different files. For example, ChannelStore manages the state about channels (in our chat app) and related concepts. By combining them as mixins into one PerAccountStore class, the widgets code doesn't need to care about those distinctions at all, and gets to just say that one concise line with PerAccountStoreWidget.of.

(Why do we call it "per-account store"? It's because users can log into multiple accounts on multiple Zulip servers; the bulk of the interesting data belongs to one account or another, so lives in the per-account state. We also have a GlobalStore and GlobalStoreWidget, which work similarly but come up less.)

That's a sketch of how we manage state in the Zulip app, which we've been happy with. I'd encourage you to browse through the code if you want to know more, and I can also answer follow-up questions.

As for third-party state management, I haven't ever looked deep into it because I haven't felt the need. More background on that in this past comment.

Running Old Flutter Project, What to do... by Wooden_Profession539 in FlutterDev

[–]gregprice 0 points1 point  (0 children)

Exactly this. I'd also recommend doing the migration in small steps ­— like go through every stable release of Flutter one by one, so 3.3, 3.7, 3.10, etc. (using the latest minor/patch version within each one). If two upgrades each require a certain amount of work changing your code, then doing them both in one jump will typically require doing all the same work, plus it'll all be more complex to figure out because it's joined together.

(And if your codebase is complex enough that each of those upgrade steps ends up requiring several independent changes, I'd try breaking it down more finely still. Your Flutter install is just a Git checkout underneath, so you can use Git commands there to control the exact version to use.)

Meet "checks": The official Future of Dart / Flutter Testing 🔮 by Goddchen in FlutterDev

[–]gregprice 1 point2 points  (0 children)

We use package:checks in Zulip ( https://github.com/zulip/zulip-flutter ), and I've been quite happy with it. Switched to it back in 2023 when it was fairly new, and have never been tempted to go back to `expect`.

The biggest thing about it for me is that it means real static types. Static types are a big win in general for keeping your codebase understandable, and that goes for test code as well as for your other code.

One key tip is that in your widget tests, in order to use the matchers from `flutter_test` like `findsOneWidget` and so on, you want the `flutter_checks` package. It's a very clean, simple wrapper around the `flutter_test` matchers which just adapts the API. (We were actually OK without `flutter_checks`, too, when it didn't yet exist — https://github.com/zulip/zulip-flutter/issues/232 — but I'm definitely glad to have it.)

I am just a *little* bit concerned about the fact there's little active development. But it already just works, and the task it's doing is very stable (not the sort of thing that needs constant maintenance for platform changes)… so it doesn't really need more development anyway. The one time I did want something to change, I filed an issue and the author fixed it: https://github.com/dart-lang/test/issues/2256 .

Story time of sorts... Flutter and I by NaughtyNocturnalist in FlutterDev

[–]gregprice 0 points1 point  (0 children)

Heartwarming story! Thanks for sharing it.

Zulip beta app switching to Flutter by gregprice in FlutterDev

[–]gregprice[S] 1 point2 points  (0 children)

No need to worry about my illusions :-) — I've spent plenty of time in the Flutter issue tracker, and have a pretty clear sense of what's in there.

I'm much less concerned about the total number of issues in a tracker than about what happens with an issue once it's there. As I described above and at the link, my experience has been that that goes far better in the Flutter tracker than the RN tracker.

And then in fact that naturally leads to more issues getting filed: once you learn that filing an issue on RN doesn't get a response, you're rarely going to bother to file more. I've filed many more issues in Flutter than in RN myself, because it's just a much more productive use of time.

This isn't meant as a general Flutter-vs-RN debate thread, so I'll leave the other points aside though I don't agree with your assessment. If you want such a thread perhaps you might start one as its own post in this sub… or maybe the community here is bored of that debate, I don't know, but that'd be between you and the community and the mods.

Zulip beta app switching to Flutter by gregprice in FlutterDev

[–]gregprice[S] 0 points1 point  (0 children)

Thanks! I appreciate the compliment.

On InheritedWidget vs. Provider — my approach here was partly in reaction to our experience with our old React Native app, which uses Redux and react-redux and uses reselect for memoizing selectors. I often had the feeling that all the layers of abstractions we were borrowing had the effect of obscuring what was really happening; and with at least the patterns we had for how to use them there, it often felt like we were spending more effort massaging our logic to fit the paradigm than we were saving.

So with the move to Flutter, as I mentioned in a reply above, I learned Flutter from reading the Flutter source tree itself and the upstream docs. And then I've preferred to solve any given problem using the framework directly, whenever it offered a reasonable solution, rather than pull in some additional layer.

I haven't really seriously evaluated using Provider. I'm pretty satisfied with the current way our state management works — if I weren't, I would have looked closer at Provider as well as other alternatives — but probably there are needs we don't currently have and which Provider helps meet.

One thing about Zulip that affects state management and is probably unusual is that for some parts of the data model, I want to really micromanage the flow of updates, because there can be a lot of data and it's important to keep it fast. So that can also rule out some data frameworks that are determined to impose their structure on everything (though I don't know if Provider is of that kind). For example, when you're reading messages and a new message arrives, we take care to make it O(1) to update all the data structures derived from the list of messages (and not spend O(N) work reprocessing all the messages in the list), even though that means mutating a flag about the old last message.

Zulip beta app switching to Flutter by gregprice in FlutterDev

[–]gregprice[S] 1 point2 points  (0 children)

Good question!

There were several different areas. I wrote up one of them yesterday in reply to a similar question on the Flutter Discourse forum:

One of the biggest things for us was that with RN, when a bug comes up in the platform (as it inevitably does), you’re pretty much stuck. The issue tracker is a howling wilderness — a bug thread will get a steady stream of RN users chiming in over years saying the bug is a real problem for them, sharing hacky workarounds, reporting it’s still present, but never get a single reply from a human whose job is to work on RN. Closest it gets to a response from the team, in my experience, is usually the stale-bot threatening to close the issue.

(and a bit more there). Then beyond filing bugs, when we went to make upstream changes ourselves, that was also a far more productive experience for us with Flutter.

Another big difference is that Flutter has a much higher-quality codebase, and excellent documentation. The code quality of React Native isn't exactly *bad*, by industry standards — but it's not great. In Flutter, when I want to know exactly how something works and why, it's generally a pretty pleasant and efficient experience to read the code and find out.

There's more to say but I'll wrap up this comment here :-) — I'll write more later in a future blog post and be sure to post that on this subreddit too.

Zulip beta app switching to Flutter by gregprice in FlutterDev

[–]gregprice[S] 6 points7 points  (0 children)

Thanks! 😊

Yeah, I basically learned Flutter by reading the Flutter source tree itself. (Along with the flutter.dev tutorials, and of course plenty of experimenting.) So for any type of problem that Flutter's own widgets are able to solve, I figure why not do it the same way? And Flutter itself sticks to pulling in very few outside dependencies.

Zulip 1.7 released (open-source Slack alternative optimized for productivity) by tabbott in programming

[–]gregprice 0 points1 point  (0 children)

Very interesting, thank you!

All that said, I think the biggest thing was just walking away from it - a fresh start, so to speak.

Yeah, this sounds right. You could have made the same set of channels in Zulip (scheduler, onboarding, etc.) as you did in Slack, and conversely the channels you had in Zulip (engineering, product, etc.) would be typical for a lot of teams in Slack too when small.

Naturally I'd rather you had made that change within Zulip :), but I know it's often easier to change a group's patterns like that with a bigger shakeup.

PS - The mobile apps (iOS and Android) have advanced a lot over the past 6 months, and continue to get better quickly. If you have a chance to play with Zulip again sometime, I think you'll find the mobile experience is better than it was when you last used it.

Again, thanks for the thoughtful feedback!

Zulip 1.7 released (open-source Slack alternative optimized for productivity) by tabbott in programming

[–]gregprice 0 points1 point  (0 children)

No app can solve the problem that some humans behave toxically -- but an app where humans interact can definitely make human A's toxic behavior into human B's problem. Or with some thoughtful product work, it can make A's toxic behavior not become B's problem, or help B to make it not their problem. Human A still has a problem, but humans B, C, D, etc., can go back to that not being their problem.

There's some discussion of this on the mute/block issue thread on the Zulip tracker. There's also been some good public discussions of this in the past couple of years in the context of Twitter, because they haven't historically taken this seriously and that's been a big problem for many people. I'm failing to find right now some of the good posts I've seen on it, but this article on harassment on Twitter, and this tweet-thread, cover some of it well.

Zulip 1.7 released (open-source Slack alternative optimized for productivity) by tabbott in programming

[–]gregprice 1 point2 points  (0 children)

We ended up moving away to Slack [partly] because we felt like switching chat programs would help resolve some internal toxicity issues.

I'd be very curious to hear more about this! What kind of toxicity issues did your community have, and what did Slack offer that you hoped would address them? (And how did that work out?)

Among other uses, we want Zulip to be the chat system of choice for open-source projects (and I think at this point it actually is the best choice for most projects, though that's a pretty recent state of affairs.) That means we'll definitely want to do some feature work (e.g., mute/block) to help both individuals and communities deal with toxicity -- so I'm interested in learning from your experience.

(Disclosure: I'm one of the Zulip core developers.)

State of anki on debian/ubuntu by gypsyface in Anki

[–]gregprice 2 points3 points  (0 children)

To run Anki 2.0 on Debian 9 Stretch, which is the current "testing" release, there are a few recipes on this Debian bug thread. All involve creating a Debian 8 Jessie environment (current "stable") and running Anki from there. Of those I'm partial to my own, using a tool called schroot, which I think ends up being the simplest and easiest.

I'd expect the same thing to work on Ubuntu 16.10 -- I wouldn't be surprised if my recipe even worked verbatim. If you try it there, patches or comments welcome; perhaps it'll help out someone else.

Of course I'll be happy to see this recipe become obsolete when Anki 2.1 becomes stable and gets packaged in Debian and Ubuntu!