Coder Social home page Coder Social logo

Comments (21)

akram-95 avatar akram-95 commented on June 21, 2024 1

@dshukertjr Oh yeah
It works now perfectly, thank you 🙏

from supabase-dart.

dshukertjr avatar dshukertjr commented on June 21, 2024 1

Realtime seems to close on its own when StreamBuilder is quickly rebuilt as stated here, so will keep this issue opened until we can properly fix that issue.

from supabase-dart.

dshukertjr avatar dshukertjr commented on June 21, 2024

@akram-95

Thanks for opening this issue. Please do follow the issue template so that we can reproduce this issue on our end. Update events seem to work on my end. If you could share a minimal code snippet to reproduce this issue, that would be great.

from supabase-dart.

akram-95 avatar akram-95 commented on June 21, 2024

here is the code of that

StreamBuilder<List<Map<String, dynamic>>>(
              stream: DatabaseHelper()
                  .client
                  .from("Questions")
                  .stream()
                  .order("timestamp")
                  .execute(),
              builder: (context, questions) {
                if (!questions.hasData) {
                  return const Center(child: CupertinoActivityIndicator());
                }
                List<Question> questionsList =
                    questions.data!.map((e) => Question.fromJson(e)).toList();

                return Expanded(
                  flex: 1,
                  child: ListView.builder(
                    shrinkWrap: true,
                    physics: const AlwaysScrollableScrollPhysics(),
                    controller: questionsAnswersController.scrollController,
                    itemCount: questionsList.length,
                    itemBuilder: (_, index) {
                      return StreamBuilder<List<Map<String, dynamic>>>(
                        stream: DatabaseHelper()
                            .client
                            .from(
                                "IndividualProfile:userId=eq.${questionsList[index].authorId}")
                            .stream()
                            .order("timestamp")
                            .limit(1)
                            .execute(),
                        builder: (context, profile) {
                          if (!profile.hasData) {
                            return const Center(child: SizedBox());
                          }
                          if (profile.hasError || profile.error != null) {
                            return const Text("Text");
                          }
                          if (profile.data!.isEmpty) {
                            return Container();
                          }

                          final dataProfile = profile.data!.first;
                          IndividualProfile individualProfile =
                              IndividualProfile.fromJson(dataProfile);
                          return widgetCard(
                              individualProfile, questionsList[index]);
                        },
                      );
                    },
                  ),
                );
              }),

from supabase-dart.

dshukertjr avatar dshukertjr commented on June 21, 2024

@akram-95
Okay, I am able to reproduce it. I haven't been able to dig all the way down to the cause of this, but my guess is that it is related to this characteristics of Supabase

While the client may join any number of topics on any number of channels, the client may only hold a single subscription for each unique topic at any given time. When attempting to create a duplicate subscription, the server will close the existing channel, log a warning, and spawn a new channel for the topic. The client will have their channel.onClose callbacks fired for the existing channel, and the new channel join will have its receive hooks processed as normal.
From supabase-js readme.md

When the outer StreamBuilder rebuilds, the inner StreamBuilders are quickly rebuilt again, causing the sockets to quickly disconnect and reconnect again.

You can probably avoid this from happening if you could combine the two Streams together to make sure this quick reconnection does not happen. I'm thinking something like the following. Note that this code is just a quick example that I wrote up, so might not work, but hopefully you can understand the idea.

This not only solves the problem you are facing, it gets rid of the flash that you experience whenever the parent widget rebuilds!

class Example extends StatefulWidget {
  const Example({Key? key}) : super(key: key);

  @override
  _ExampleState createState() => _ExampleState();
}

class _ExampleState extends State<Example> {
  late StreamSubscription _questionsListener;

  // Stores the StreamSubscription where the userId is the key of the map
  final Map<String, StreamSubscription> _profileSubscriptions = {};

  // Stores the profile data of users where the key is their uid
  final Map<String, Profile> _profiles = {};

  final List<Question> _questions = [];

  @override
  Widget build(BuildContext context) {
    return ListView.builder(itemBuilder: (context, index) {
      final question = _questions[index];
      return ListTile(
        title: Text(question.title),
        subtitle: Text(_profiles[question.authorId].name),
      );
    });
  }

  void _loadData() {
    _questionsListener =
        supabase.from('questions').stream().execute().listen((questions) {
      _questions.addAll(Question.fromEvent(questions));

      for (final question in questions) {
        // Make sure only create the listener if a listener for the user has not been created yet
        if (_profileSubscriptions[question['authorId']] == null) {
          _profileSubscriptions[question['authorId']] = supabase
              .from('IndividualProfile:userId=eq.${question['authorId']}')
              .stream()
              .execute()
              .listen((event) {
            final profile = Profile.fromEvent(event);
            _profiles[profile.uid] = profile;
            setState(() {});
          });
        }
        setState(() {});
      }
    });
  }

  @override
  void initState() {
    _loadData();
    super.initState();
  }

  @override
  void dispose() {
    _questionsListener.cancel();
    for (final profileListener in _profileSubscriptions.values) {
      profileListener.cancel();
    }
    super.dispose();
  }
}

from supabase-dart.

dshukertjr avatar dshukertjr commented on June 21, 2024

Will dig deeper into this issue and share anything that I find here in the meanwhile!

from supabase-dart.

akram-95 avatar akram-95 commented on June 21, 2024

@dshukertjr Thank you so much for your support , but unfortunately it doesn't work and I did notice that when I update an array of varchar , it's not empty, it has the value [archa]
From where does it come ?

from supabase-dart.

akram-95 avatar akram-95 commented on June 21, 2024

when i update the array of char stars , in the result of execution it's true but in the listen of stream, i get the value [archa]

image

from supabase-dart.

dshukertjr avatar dshukertjr commented on June 21, 2024

@akram-95

Could you please share your table schema as well as the code you are using to produce the above log?

from supabase-dart.

akram-95 avatar akram-95 commented on June 21, 2024

Yeah please https://user-images.githubusercontent.com/55693316/130314658-f0b05b39-ab8f-4817-8ad1-1f9382754fdd.MOV

from supabase-dart.

akram-95 avatar akram-95 commented on June 21, 2024

@dshukertjr On the table the array stars is empty and when I update it , it becomes an new user Id when I want to listen to that I get [archa]

from supabase-dart.

akram-95 avatar akram-95 commented on June 21, 2024

the code for updating a Question
image
the code for listening to changes in the table Questions
image

from supabase-dart.

dshukertjr avatar dshukertjr commented on June 21, 2024

@akram-95

When you share your code, it is better to share it as text rather than image. That way other people can just copy and paste!

In the first code block, could you share what you are assigning to newQuestionValue?

Also, is the reason why you have stars and likes etc... in Questions table because that is how you used to do it in Firebase? Typically in SQL database, you do it like this? With this approach, you can utilize row level security as well. With your current setup, I think it's impossible or at least pretty hard to make it completely secure with row level security. You always face malicious user wiping the star or like data.

from supabase-dart.

akram-95 avatar akram-95 commented on June 21, 2024

i used stars and likes as an array for every Question , that's the only reason , i did it always like that using firebase
that's the code for the first Block

  Future setLikeQuestionBySupabase(
      Question question, likeArt likeart, String followerId) async {
    var result = await client
        .from("Questions")
        .select()
        .eq("questionId", question.questionId)
        .single()
        .execute();

    if (result.error != null) {
      return ProcessState.failed;
    }

    Question newQuestionValue = Question.fromJson(result.toJson()["data"]);

    if (likeart == likeArt.star) {
      newQuestionValue.dislikes.removeWhere((element) => element == followerId);
      newQuestionValue.likes.removeWhere((element) => element == followerId);
      if (newQuestionValue.stars.contains(followerId)) {
        newQuestionValue.stars.removeWhere((element) => element == followerId);
      } else {
        newQuestionValue.stars.add(followerId);
      }
    }
    if (likeart == likeArt.like) {
      newQuestionValue.stars.removeWhere((element) => element == followerId);
      newQuestionValue.dislikes.removeWhere((element) => element == followerId);
      if (newQuestionValue.likes.contains(followerId)) {
        newQuestionValue.likes.removeWhere((element) => element == followerId);
      } else {
        newQuestionValue.likes.add(followerId);
      }
    }
    if (likeart == likeArt.dislike) {
      newQuestionValue.likes.removeWhere((element) => element == followerId);
      newQuestionValue.stars.removeWhere((element) => element == followerId);
      if (newQuestionValue.dislikes.contains(followerId)) {
        newQuestionValue.dislikes
            .removeWhere((element) => element == followerId);
      } else {
        newQuestionValue.dislikes.add(followerId);
      }
    }

    result = await client
        .from("Questions")
        .update(newQuestionValue.toJson())
        .eq("questionId", newQuestionValue.questionId)
        .execute();
    print(result.data);
    if (result.error != null) {
      return ProcessState.failed;
    }

    return ProcessState.successful;
  }

from supabase-dart.

dshukertjr avatar dshukertjr commented on June 21, 2024

@akram-95
Thanks for sharing the code! I found a bug within realtime-dart that is causing the array value not being returned correctly and returning ['archa'] instead. Have to go to bed for now, but will fix it first thing in the morning! Thanks for the patients!

from supabase-dart.

akram-95 avatar akram-95 commented on June 21, 2024

@dshukertjr Nice to hear
Good night and thank you for your support

from supabase-dart.

dshukertjr avatar dshukertjr commented on June 21, 2024

@akram-95
With the latest update, you should be able to receive array values correctly. Thanks for your patients!

from supabase-dart.

elhe26 avatar elhe26 commented on June 21, 2024

Realtime seems to close on its own when StreamBuilder is quickly rebuilt as stated here, so will keep this issue opened until we can properly fix that issue.

Any updates?

from supabase-dart.

dshukertjr avatar dshukertjr commented on June 21, 2024

Hi @elhe26
We have been busy adding WALRUS support to the realtime client, so there has not been any updates on this issue, but did you try the workaround? It is generally not a very good idea to nest StreamBuilders anyway in my opinion because every time the parent rebuilds, the child has to rebuild again and has to start from ConnectionState.waiting.

from supabase-dart.

bdlukaa avatar bdlukaa commented on June 21, 2024

stream() should not be called directly on the build function. When it's rebuilt, the stream is executed every time the screen is rebuilt (since you're calling the function on build time). The stream() will also rebuilt when you hot realod, so this is not an issue with the current stream() implementation.

What you can do is:

class Example extends StatefulWidget {
  const Example({Key? key}) : super(key: key);

  @override
  _ExampleState createState() => _ExampleState();
}

class _ExampleState extends State<Example> {
  late Stream stream1;
  late Stream stream2;

  @override
  void initState() {
    super.initState();
    stream1 = DatabaseHelper()
        .client
        .from("Questions")
        .stream()
        .order("timestamp")
        .execute();
    stream2 = DatabaseHelper()
        .client
        .from("IndividualProfile:userId=eq.${questionsList[index].authorId}")
        .stream()
        .order("timestamp")
        .limit(1)
        .execute();
  }

  @override
  void dispose() {
    // there isn't such thing as disposing streams. this is handled internally
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return ListView.builder(itemBuilder: (context, index) {
      final question = _questions[index];
      StreamBuilder<List<Map<String, dynamic>>>(
          stream: stream1,
          builder: (context, questions) {
            if (!questions.hasData) {
              return const Center(child: CupertinoActivityIndicator());
            }
            List<Question> questionsList =
                questions.data!.map((e) => Question.fromJson(e)).toList();

            return Expanded(
              flex: 1,
              child: ListView.builder(
                shrinkWrap: true,
                physics: const AlwaysScrollableScrollPhysics(),
                controller: questionsAnswersController.scrollController,
                itemCount: questionsList.length,
                itemBuilder: (_, index) {
                  return StreamBuilder<List<Map<String, dynamic>>>(
                    stream: stream2,
                    builder: (context, profile) {
                      if (!profile.hasData) {
                        return const Center(child: SizedBox());
                      }
                      if (profile.hasError || profile.error != null) {
                        return const Text("Text");
                      }
                      if (profile.data!.isEmpty) {
                        return Container();
                      }

                      final dataProfile = profile.data!.first;
                      IndividualProfile individualProfile =
                          IndividualProfile.fromJson(dataProfile);
                      return widgetCard(
                          individualProfile, questionsList[index]);
                    },
                  );
                },
              ),
            );
          });
    });
  }
}

Can this be closed?

from supabase-dart.

bdlukaa avatar bdlukaa commented on June 21, 2024

Closing as solved!

from supabase-dart.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.