Technical Debt and Streams/BLoC (The Boring Flutter Development Show, Ep. 4)

Поділитися
Вставка
  • Опубліковано 15 вер 2024
  • In this episode, Filip and Matt start by erasing some of the technical debt that the Hacker News reader app accumulated in the past 3 episodes. They remove the new keyword everywhere, delete unused files, and re-enable unit tests.
    Then they set out to tackle state management and separation of concerns between view logic and business logic. They choose to follow the BLoC pattern, using Streams and ReactiveX (rxdart).
    With that in place, they implement a new feature: ability to switch between the listing of top and new stories.
    Let us know your thoughts and requests for future episodes in the comments below or on Twitter using #BoringShow.
    Watch more episodes of the boring show here → bit.ly/BoringShow
    Get started with Flutter → flutter.io
    Try a Flutter codelab → goo.gl/d3fHPo
    Join the conversation → goo.gl/68oUnb
    Subscribe to the Google Developers channel → goo.gl/mQyv5L

КОМЕНТАРІ • 138

  • @brennangambling8907
    @brennangambling8907 5 років тому +126

    Streams/BLoC and State management starts at 18:45.

    • @RaedMughaus
      @RaedMughaus 5 років тому +5

      I wish I have read the comments before 😭😭

    • @asif_mojtoba
      @asif_mojtoba 5 років тому +9

      *not all superhero wear capes.*

    • @MuhdAimanRS
      @MuhdAimanRS 5 років тому

      thanks bro

    • @YabbaDadADo
      @YabbaDadADo 5 років тому +3

      People like you make the internet nice again :)

    • @anikhasan6729
      @anikhasan6729 4 роки тому +1

      After 5 mins of watching the video, I had a feeling that someone might have commented this , I checked the comment section and found this. heheh thanks a lot

  • @redbrogdon
    @redbrogdon 6 років тому +272

    Wait, you did one without me?

    • @vinceramcesoliveros6739
      @vinceramcesoliveros6739 6 років тому

      Andrew Brogdon where were you when they needed you?

    • @semicognitive
      @semicognitive 6 років тому

      João Gabriel Coutinho Dude that's kinda mean

    • @yodartt
      @yodartt 6 років тому +1

      I guess you're right, my bad on that one :/

    • @RogerVargas0
      @RogerVargas0 6 років тому +9

      Please come back with your dark IntelliJ

    • @brzhang109
      @brzhang109 6 років тому

      haha ,you the boss

  • @임창수-c7c
    @임창수-c7c 5 років тому +90

    We need an official guide for Bloc pattern.

  • @saraweber312
    @saraweber312 5 років тому +33

    Watching the flutter devs struggle makes me feel a lot better about my own code 😅

  • @youkokaku
    @youkokaku 5 років тому +3

    "This is all commented out code? Oh I love deleting code." Best comment ever, Filip!

  • @tushargupta6533
    @tushargupta6533 3 роки тому

    This video saved my life 😭😭😭😭 I wasn't able to understand this thing but you guys explained it in a very very simple way🙏🏻 please continue this series

  • @hadifromlebanon3812
    @hadifromlebanon3812 4 роки тому +1

    That switch to vscode is visually relaxing

  • @mrbam8
    @mrbam8 4 роки тому +2

    I give Google tons of credit for being brave enough to make these videos... can't be easy.. By the way, Thanks Apple!

  • @mokhosh
    @mokhosh 6 років тому +2

    So glad you're back Philip :) Missing Andrew though, maybe you can do a three way (just flutter).

  • @apf14378
    @apf14378 6 років тому +2

    Suggestion for the next show:
    1.The list should not be updating completely each time you change stories type, each new item retrieved from the network should cause a bloc stream update with a new list containing just one more item and, if there is still more news waiting for retrieval, the list should also include "working" indicator as last item. I don't think anything else would be 'acceptable' for a serious app.
    2. You should go for inheritable widget, as it's what you are going to do in any other "non just playing" app.

  • @benjaminli21
    @benjaminli21 2 роки тому +1

    I know it's kind of too late now... but I think it might be better for adding the links of the package using in the episode in the video description.

  • @lucasnantes6484
    @lucasnantes6484 4 роки тому

    It would be nice to hear more about BLoC Strengths and weakness and also an example of BLoC for configuration of In-App language

  • @NivenShah
    @NivenShah 5 років тому +1

    Hi team - With BLoC pattern, what is the advantage behind strictly using sinks for inputs rather than void functions that do not return a result (ie, the result comes through a stream)? By using sinks for inputs, you need to create a sink, listen to the sink, and manage all the subscriptions internally instead of just calling out to a service. It seems like this (sink) approach requires more lines of code and more careful thought and attention from the developer to ensure s/he is managing all of it correctly. Thank you for these wonderful long-form videos by the way - what a great learning resource!

    • @filiphracek
      @filiphracek 5 років тому +2

      Great question. The idea with Sink/Stream is, in my opinion, mainly important only if you're going all Rx / functional. At that point, it's easier to pipe things, and it's also conceptually easier to deal with the pattern when you know _everything_ is an asynchronous Sink or Stream. Additionally, asynchronous errors correctly propagate through the stream.
      That said, not everyone goes all Rx / functional (like, for example, the app we're building above). So it depends on how big of a purist you (and your team) want to be. I personally value simplicity over purity, so I wouldn't bat an eye if I saw someone using the adding a synchronous method to a bloc.
      We will be getting back to state management in the Boring Show shortly, trying to do just that: make it as simple as possible.

    • @NivenShah
      @NivenShah 5 років тому

      @@filiphracek Thanks Filip!

  • @sreejithsreejayan441
    @sreejithsreejayan441 3 роки тому

    What happens if we create the bloc many widgets(Like creating the bloc in home_page or something) down instead of passing through the constructor by constructor.

  • @JSANL
    @JSANL 6 років тому +2

    Absolutly Love this show!

  • @omidnejadabbasi2171
    @omidnejadabbasi2171 2 роки тому

    Question: With this method the UI will wait untill all the items has ben fetched and that might take a logn time, how about a video that addresses that. and also how to animate items entering the list?

  • @Mohith7548
    @Mohith7548 6 років тому +12

    #FlutterBoringDevelopmentShow () => Bloc pattern is not clear and is difficult to understand. Please make a detail concept-clear video on Bloc Pattern. Hey did anyone notice the white board?

    • @filiphracek
      @filiphracek 6 років тому +15

      We always forget the white board :)
      Yeah, we hope to do a focused video on the Bloc pattern. But that might take some time, because distilling something like that into a video takes a lot of thinking.
      In the interim, here are some resources that might be useful (unless you already know about them, of course):
      - original I/O session: ua-cam.com/video/RS36gBEp8OI/v-deo.html
      - companion article: medium.com/flutter-io/build-reactive-mobile-apps-in-flutter-companion-article-13950959e381
      - companion repo: github.com/filiph/state_experiments/
      - an article by Amrut Patil: codeburst.io/state-management-using-bloc-pattern-in-flutter-390d4056006f

    • @Mohith7548
      @Mohith7548 6 років тому

      Thanks for the sources. I've been pushing hard myself to understand the Bloc pattern since this morning. I missed the codelabs.

    • @AndrewMooreReactle
      @AndrewMooreReactle 6 років тому +3

      Instead of attributing amrut, you should really attribute Stephen grider and his udemy course which that article is a blatant copy of. I found the udemy course really helped my understand the bloc concept.

    • @ukaszhuculak7854
      @ukaszhuculak7854 6 років тому +1

      I really like the show.
      I am particularly interested in the lifecycle of BLoCs. If the BLoC opens connections to external resources, it should also close them down. Is there any other way than to use InheritedWidget as a BLoC supplier for internal StreamBuilders (example: github.com/brianegan/flutter_architecture_samples/blob/master/example/bloc_flutter/lib/main.dart)?

    • @LukePighetti
      @LukePighetti 6 років тому

      bloc pattern is very simple once you get used to it
      input functions/streams >> perform actions on data >> output streams
      That's Bloc in a nutshell. Everything else is idiomatic.

  • @raycharlz4937
    @raycharlz4937 3 роки тому

    I did the same code but the line "key: Key(article.text)" in _buildItem Widget caused a duplicate key error that doesn't appear in the show... why?

  • @fullemptiness
    @fullemptiness 5 років тому +3

    I appreciate your work, but I'm hoping you make the non-boring show with the edit, less re-work and more preparing and focusing. This will help me in the few hours I only have in weekend.

  • @abolfazlrezaei2801
    @abolfazlrezaei2801 3 роки тому

    These guys are great

  • @kentonraiford7784
    @kentonraiford7784 5 років тому

    Thanks for doing this! We need more tutorials on BloC and rxdart.

  • @argigentarna
    @argigentarna 5 років тому

    Really nice Tutorial, i love the boring show. Hope the next tutorial build modify list view, llike button or something else.

  • @michelfeinstein
    @michelfeinstein 5 років тому

    It's better to look for "Material Icons" on Google, than on the Flutter docs... It's in a grid, in the docs they are one per line, which makes the searching really tedious.

  • @darshangowda309
    @darshangowda309 6 років тому +1

    Hey guys! so quick question, using a textfiled / textformfield inside a stream builder refreshes the entire builder widget. How do I use something like a form inside a stream builder with data pre populated ? Is there a work around ?

    • @sanathnherath
      @sanathnherath 5 років тому

      same thing happen to me but no solution yet, did you find solution

  • @reilem
    @reilem 5 років тому +2

    23:15 I was literally using StreamControllers and had this exact issue and was looking for a way to fix it. Thanks :D

  • @harshadmanglani1309
    @harshadmanglani1309 4 роки тому

    If we use streams, and let's say the API updates the IDs to put like another story on the top, will my app change in real time? I'm a bit confused with streams.
    Basically, I'm trying to ask if streams make periodic calls to the API to monitor changes on their own and update if necessary (much like an Instant Messaging App)

  • @PraveenAV
    @PraveenAV 6 років тому

    Very very useful. Thanks friends.

  • @hiaw
    @hiaw 6 років тому

    Did you figure out how to run the skipped test? Quick search didn't get anything for me.

  • @JeremyConnor
    @JeremyConnor 6 років тому

    Great show, thanks team!

  • @Mohith7548
    @Mohith7548 6 років тому +7

    I like the FlutterBoringShow. I want to throw light on the bug that makes me drink coffee (while the gradle builds). THe bug is, whenever you stop the app and run again, it always builds the previous version of the app. I have to manuallty do "flutter clean" in the terminal and run again. Then it takes a lot of time to build (as you know gradle is a timer lover). Please fix issue ASAP.
    #LoveFlutter

    • @filiphracek
      @filiphracek 6 років тому

      Is this filed on github.com/flutter/flutter/issues? I _think_ I've seen this before, but can't reproduce.
      As a temporary workaround, does triggering a hot reload (or hot restart) work. In other words: stop app, start again, trigger hot reload (press 'r'/'R' in command line, or click the appropriate button in an IDE). If not, the next workaround would be to make a meaningless change (like temporarily adding a blank line somewhere), then saving.

    • @Mohith7548
      @Mohith7548 6 років тому

      Filip Hráček Yeah! I'm saving time by do the same, making some meaningless changes and hot restart. But when I'm modifying code in state function (initState()) it doesn't make sense to do hot reload in that case. I need to delete the build folder and the build the app again. I don't understand why the problem has arrived, but it wasn't there in previous versions of flutter. Hoping that it will be fixed soon :)

    • @Mohith7548
      @Mohith7548 6 років тому

      Here is the issue. It was already filed. github.com/flutter/flutter/issues/16604

    • @Mohith7548
      @Mohith7548 6 років тому

      I'm on Beta channel. On which channel are you Phillip Sir?

    • @davidloera9202
      @davidloera9202 6 років тому

      0

  • @truongsinhtran-nguyen7129
    @truongsinhtran-nguyen7129 5 років тому +1

    6:12 www.git-tower.com for those who are also Interested in the tool for source version control the show hosts are using. I'm interested because I'm currently using SourceTree

  • @Alex-fs6kz
    @Alex-fs6kz 5 років тому

    Is there a public repo available with this code? I would like to review what you created in this episode because scrubbing the video is a bit difficult.

  • @unityme8898
    @unityme8898 6 років тому

    hello guys, can you help me on this ??? ----> How do we use the single global instance of Bloc to such a way that, the submit button will only enable for user to click when both email and password stream contains no error?

  • @sanathnherath
    @sanathnherath 5 років тому

    I am developing user profile update page for a app, I got problem there, To populate form with initial data I have used StreamBuilder and used TextEditingController load data to TextField, data loading fine and works well, but problem is when I try to change TextField and focus to next TextField StreamBuilder refresh the ui with tha data which load previously and lost edited data. In this case Stream does not updated from network call. Anybody came across this situation ? or any recommended way to do this?. Please help.

  • @murkyglasses
    @murkyglasses 6 років тому +1

    When using bloc pattern and firebase, is it advisable to move the stream firebase call to bloc also? and if so how would you do it?

    • @filiphracek
      @filiphracek 6 років тому +4

      I would advise it, yes. I don't think your view logic should care about the implementation details of Firebase connections, references, etc.
      There are many ways to do this, and none of them is - in itself - better than others. The simplest is to encapsulate the Firebase code in the bloc. Initiate the connection with Firebase/Firestore in the constructor of the Bloc (and, if your bloc has shorter lifecycle than your app, don't forget to close it in a close() method). Then translate the inputs to the Bloc to Firebase calls (if needed), and firebase callbacks to bloc outputs (again, only if needed).
      A more robust (and testable) approach would be to inject the firebase service to the bloc. So our main method would look something like:
      void main() {
      final dbService = MyFirebaseService();
      final myBloc = MyBloc(dbService);
      runApp(MyApp(myBloc));
      }

    • @murkyglasses
      @murkyglasses 6 років тому +1

      Thanks for the advise, I'm starting to move my Firestore calls to bloc and just started using built value. I'm just a bit confused about closing bloc/firestore? do you know any articles I can refer to about it? I assumed it will automatically be garbage collected after widget is gone?

  • @moveaxebx
    @moveaxebx 5 років тому

    Why mixing dart streams with rx observables in the bloc? StreamController vs. BehaviourSubject...

  • @ShubhamSoni-wo9by
    @ShubhamSoni-wo9by 6 років тому

    The main problem i found with bloc pattern and Stream builder is that When you have bunch of pages in the app and the view of the page depends upon the stream of data coming in according to the sink of input object. If there is any network call between sink of input and stream of output then the page will show the old data that was on the stream untill it get a new data. And we know internet connection can be very pathetic.
    Is there any way to show some CircularProgressIndicator untill the stream data is updated.

    • @glitch3dout
      @glitch3dout 5 років тому +1

      The way I solved this is by emptying the stream by pushing in an empty list before doing the network call, and then after the call is successful, I update the stream with the data. Meanwhile in the UI, whenever the stream is empty, the CircularProgressIndicator is displayed, and when it has data, the data is displayed.

  • @muhammadchhota4463
    @muhammadchhota4463 6 років тому

    What is use of immutable object? In the news application it's making sense that once news articles is created then it won't change.but in some applications we need to change the state of object. Example shopping cart or notes app. How to use immutable object in those cases?

    • @andrewbrogdon558
      @andrewbrogdon558 6 років тому +2

      One of the big advantages of immutable objects is that you know they can't change without your code knowing it. If you have a mutable object referenced in two places in your code, for example, one of them could change it without the other getting notified or being aware, so to speak. With immutables, once you receive an object, you can code knowing that it won't change, and you won't need to update the UI until you receive a new object from whatever source is generating them (a stream, for example).
      As far as making changes goes, while you can't modify an object, you *can* create a new object that's a modified copy of the old one. The built_value package uses methods called "rebuild" for this: medium.com/dartlang/darts-built-value-for-immutable-object-models-83e2497922d4#5f61

  • @greywolf1632
    @greywolf1632 5 років тому

    Hey Matt, could you please share what color theme you're using in VSCode at 46:19 ? It looks nice.

  • @ramaagusdwiputra1960
    @ramaagusdwiputra1960 3 роки тому

    State management starts at 18:48

  • @amanihamila6305
    @amanihamila6305 6 років тому +1

    Anyone has an idea about how to use BloC pattern in angular Dart ? i'm trying to implement code sharing, so i gotta understand that first to be able to use my blocs/common code in angular dart.

    • @Mzulfreaky
      @Mzulfreaky 6 років тому

      Amani Hamila ahh i have beem thinking about code sharing too! Please tell me if you find a way on it! Thanks in advance

  • @randlemcmurphy6293
    @randlemcmurphy6293 5 років тому

    In clean architecture, which layer do blocs belong to? Domain layer? Or presentation layer?

    • @remmievail8148
      @remmievail8148 5 років тому +1

      From what I understand, blocs provide data, execute business logic, but not actual logic for how data is displayed. From that standpoint, I would not include them in the presentation layer.

  • @mohabmagdyshokryabdel-hame3203
    @mohabmagdyshokryabdel-hame3203 5 років тому

    Filip, please share with us your live templates they are great and save me much time I just need to copy them and paste them on my IDE and thanks guys for this great unBoring show =)

    • @bacharsaleh6984
      @bacharsaleh6984 5 років тому

      add these to live templates
      Stream get $OBJECT_NAME$ => _$OBJECT_NAME$Subject.stream;
      final _$OBJECT_NAME$Subject = BehaviorSubject;

  • @poluxsaurus1454
    @poluxsaurus1454 5 років тому +1

    Bloc pattern gets considerably friendlier with a library like github.com/felangel/bloc

  • @顾吉涛
    @顾吉涛 5 років тому

    Any one know ? where could I clone the demo code of the BoringShow?

  • @MuhammadFahreza
    @MuhammadFahreza 6 років тому

    What is the purpose of Unmodifiable List View ? Why we could not use ListView instead ?

    • @murkyglasses
      @murkyglasses 6 років тому

      As I understand it, you don't want others (including other class or view) to update your list data (outside the bloc). So you convert it to unmodifiable list.
      I think this is a good practice, as your views will only consume data and any modification should be done on bloc. Will definitely keep this in mind when I revamp my flutter project.

  • @pashute12
    @pashute12 5 років тому

    Nice. A small remark: Glaring issues are pronounced GLERRing issues

  • @RichardVowles
    @RichardVowles 6 років тому +1

    Why do you keep exiting IDEA to commit? Why not just type Command-K and go from there?

    • @UCjNrKLyRJI-abFA8qiNo92Q
      @UCjNrKLyRJI-abFA8qiNo92Q 6 років тому

      They can either right-click->commit or right down "git" at the terminal

    • @RichardVowles
      @RichardVowles 6 років тому

      Pirate command-k is considerably easier

  • @ride4sun
    @ride4sun 5 років тому

    I am wondering how you get the code completion for the Stream. Do you have templates loaded for that?

    • @bacharsaleh6984
      @bacharsaleh6984 5 років тому

      add these to live templates
      Stream get $OBJECT_NAME$ => _$OBJECT_NAME$Subject.stream;
      final _$OBJECT_NAME$Subject = BehaviorSubject;

  • @ShubhamSoni-wo9by
    @ShubhamSoni-wo9by 6 років тому

    Another problem with builders is that we can't merge streams in it. I tried StreamGroup.merge and I also tried with Observables. By using Observation1.mergewith([observable2]).
    In both the cases the data shown by the view is of only single Stream or observable .
    I tried lazy streams too it also didnt work please show a way to merge 2 streams and use a one stream in the stream builder.

    • @remmievail8148
      @remmievail8148 5 років тому

      Your bloc should perform the work of merging two steams into one, so the builder only has to consume 1 stream.

  • @nihaddelic5032
    @nihaddelic5032 5 років тому

    How to get t-shirts, sticker for flutter and dart? Or bird toy?

  • @shamscorner
    @shamscorner 3 роки тому

    12:25 And that's how Windows developers deal with technical debt. 😆😄

  • @satishkumar-qq8df
    @satishkumar-qq8df 5 років тому

    Our team has created a paint application, we are storing to JSON file. actually what happening after drawing 15 to 20 minutes drawing gets slow.
    Can you suggest me some best way that we could build better app.

    • @nothappyz
      @nothappyz 5 років тому +2

      Are you seriously using jsons to store a picture?...

  • @seanbruceful
    @seanbruceful 6 років тому

    very fun and cool show. I am a newbie flutter developer and I'm very glad to see more and more flutter stuffs in google develepers channel. I also have a question about this episode which is Can I and should I use built_collection to replace unmodifiableListView, what's the adventage and disadventage of them?

    • @filiphracek
      @filiphracek 6 років тому

      Good question! You can definitely use built_collection (such as BuiltList, in this case) instead of UnmodifiableListView.
      The advantage of BuiltList is that it guarantees some things that UnmodifiableListView doesn't. For example, by default, Built objects cannot be null. Also, they're immutable all the way down (while an UnmodifiableListView could contain in it objects that you can modify, and UnmodifiableListView can't prevent you from doing that). Another advantage is that serialization is easy to add, which might be something you care for.
      The disadvantage is that it's (generally) more code. UnmodifiableListView is just that one wrap around your list and that's it.

    • @seanbruceful
      @seanbruceful 6 років тому

      😄Thank you Filip

  • @johnchen4486
    @johnchen4486 6 років тому

    where is the source code of the example?

  • @athosdomotics7104
    @athosdomotics7104 3 роки тому

    Hello there,
    Thanks for the videos,
    Where can I get your Flutter T-shirt ?
    Regards

  • @DakshGargas
    @DakshGargas 6 років тому

    Why didn't you use `StreamController` instead of `Sink`?

    • @brzhang109
      @brzhang109 6 років тому

      because ,they are the same.. StreamController use sink add event...

  • @lintonachmad6410
    @lintonachmad6410 6 років тому +1

    Add Google Maps 🗣🗣

  • @UCjNrKLyRJI-abFA8qiNo92Q
    @UCjNrKLyRJI-abFA8qiNo92Q 6 років тому

    Talk about Google Maps, what have you done?

  • @rubensdemelo
    @rubensdemelo 5 років тому

    20:43 create bloc class

  • @sunilk9760
    @sunilk9760 4 роки тому

    they are using rxdart instead of StreamController
    funny

  • @jibraniqbal7830
    @jibraniqbal7830 5 років тому

    this shows how much mess coding can be even though, you are google developer, kodus to all the developer out their, specifically mobile ones.

  • @biezhi
    @biezhi 6 років тому

    Thank you share 🍭🍭

  • @JacobPhillips
    @JacobPhillips 6 років тому

    Is there a repo link?

    • @filiphracek
      @filiphracek 6 років тому +1

      Sure: github.com/filiph/hn_app

    • @marconapoli1212
      @marconapoli1212 6 років тому

      The github repo is at github.com/filiph/hn_app

  • @yaswanthamaraneni7529
    @yaswanthamaraneni7529 5 років тому

    Isn't it cool, if visual studio code can give suggestions to add dependencies in pubspec.yml file?
    for example, at ua-cam.com/video/fahC3ky_zW0/v-deo.html, behaviourSubject needs rxdart dependency to be added in pubspec.yml, so if VS code can show dropdown suggestions there will be nice a feature.

  • @Mohith7548
    @Mohith7548 6 років тому +4

    I have to make playback speed as 1.5x ;)

    • @filiphracek
      @filiphracek 6 років тому +1

      That's the best (least painful) way to watch this show. We should probably mention that in the video. The option to speed up YT videos is not that well known.

    • @franckadonis2623
      @franckadonis2623 6 років тому

      2X man

  • @jpkontreras
    @jpkontreras 5 років тому

    i need the font name!!!!

    • @filiphracek
      @filiphracek 5 років тому +1

      It's called Input Mono.

  • @FernandoRosentalski
    @FernandoRosentalski 5 років тому

    hey devin, play us a song

  • @Davo2able
    @Davo2able 6 років тому

    Can you guys use a dark theme? The current theme is hard to look at in low light situations.

  • @digitart-media
    @digitart-media 6 років тому +1

  • @furkanvatandas4819
    @furkanvatandas4819 6 років тому

    This topic is really confused

  • @Dorumin
    @Dorumin 5 років тому

    Have you been writing rust code?

  • @triandisunarya8841
    @triandisunarya8841 6 років тому

    flutter demo crash a lot in my lolipop smartphone when i try to check it out

    • @filiphracek
      @filiphracek 6 років тому

      That is definitely a concern. Could you please file an issue at github.com/flutter/flutter ?

  • @shivankgoel
    @shivankgoel 4 роки тому

    Hi, I am trying to reproduce this but I am having issues as snapshot is always staying in the Connectionstate.waiting. Could anyone please help me debug this. More information here: stackoverflow.com/questions/62143711/streambuilder-snapshot-is-not-getting-out-of-waiting-state-while-using-bloc

  • @TheKarelians
    @TheKarelians 4 роки тому

    Might be one of the worst technical video productions. Try listening to the video without looking at the screen and you will see what I mean. There is absolutely no narrative explaining what you are trying to accomplish...

  • @hackerunet
    @hackerunet 5 років тому

    Too boorinnggggggg!!!