profile.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. import { Component, ViewChild } from "@angular/core";
  2. import {
  3. IonicPage,
  4. NavController,
  5. NavParams,
  6. InfiniteScroll,
  7. Content,
  8. LoadingController
  9. } from "ionic-angular";
  10. import { TwitterApiProvider } from "../../providers/twitter-api/twitter-api";
  11. import { P2pStorageIpfsProvider } from "../../providers/p2p-storage-ipfs/p2p-storage-ipfs";
  12. import { P2pDatabaseGunProvider } from "../../providers/p2p-database-gun/p2p-database-gun";
  13. /**
  14. * Generated class for the ProfilePage page.
  15. *
  16. * See https://ionicframework.com/docs/components/#navigation for more info on
  17. * Ionic pages and navigation.
  18. */
  19. @IonicPage()
  20. @Component({
  21. selector: "page-profile",
  22. templateUrl: "profile.html"
  23. })
  24. export class ProfilePage {
  25. user: any = [];
  26. tweets: any[];
  27. oldestLoadedTweetId;
  28. enableRefresh: boolean = true;
  29. enableInfiniteScroll: boolean = true;
  30. @ViewChild(Content)
  31. content: Content;
  32. constructor(
  33. public navCtrl: NavController,
  34. private loadingCtrl: LoadingController,
  35. public navParams: NavParams,
  36. private twitter: TwitterApiProvider,
  37. private ipfs: P2pStorageIpfsProvider,
  38. private gun: P2pDatabaseGunProvider
  39. ) {}
  40. ionViewDidLoad() {
  41. // Show loading indicator
  42. const loading = this.loadingCtrl.create();
  43. loading.present();
  44. // Read user id
  45. const userId = this.navParams.get("userId");
  46. // Fetch user details from Twitter
  47. this.twitter.fetchUser(userId).then(res => (this.user = res));
  48. // Load user's timeline from Twitter and P2P
  49. this.loadTimeline(userId).then(res => {
  50. if (res.length > 0) {
  51. // Store tweets
  52. this.tweets = res;
  53. // Save oldest tweet's id for next load more
  54. this.oldestLoadedTweetId = res
  55. .filter(tweet => !tweet.private_tweet)
  56. .reduce((acc, cur) => (acc.id < cur.id ? acc : cur))["id"];
  57. }
  58. // Hide loading indicator
  59. loading.dismiss();
  60. });
  61. }
  62. doRefresh(refresher) {
  63. this.loadTimeline(this.user.id).then(res => {
  64. if (res.length > 0) {
  65. // Replace tweets
  66. this.tweets = res;
  67. // Save oldest tweet's id for next load more
  68. this.oldestLoadedTweetId = res
  69. .filter(tweet => !tweet.private_tweet)
  70. .reduce((acc, cur) => (acc.id < cur.id ? acc : cur))["id"];
  71. }
  72. // Hide loading icon
  73. refresher.complete();
  74. });
  75. }
  76. loadMore(infiniteScroll: InfiniteScroll) {
  77. if (this.enableInfiniteScroll) {
  78. this.loadTimeline(this.user.id, this.oldestLoadedTweetId).then(res => {
  79. if (res.length > 0) {
  80. // Append loaded tweets
  81. this.tweets = this.tweets.concat(res);
  82. // Save oldest tweet's id for next load more
  83. this.oldestLoadedTweetId = res
  84. .filter(tweet => !tweet.private_tweet)
  85. .reduce((acc, cur) => (acc.id < cur.id ? acc : cur))["id"];
  86. }
  87. // Hide loading icon
  88. infiniteScroll.complete();
  89. });
  90. } else {
  91. // Hide loading icon
  92. infiniteScroll.complete();
  93. }
  94. }
  95. private async loadTimeline(userId, oldestLoadedTweetId?) {
  96. // Fetch tweets from Twitter
  97. const tweets =
  98. oldestLoadedTweetId == null
  99. ? await this.twitter.fetchUserTimeline(userId)
  100. : await this.twitter.fetchUserTimelineSince(
  101. userId,
  102. oldestLoadedTweetId
  103. );
  104. // Determine end of time interval to look for private tweets
  105. let intervalEnd: Date;
  106. if (tweets.length < 20) {
  107. // End of timeline is reached
  108. this.enableInfiniteScroll = false;
  109. intervalEnd = new Date("2018-04-01T00:00:00");
  110. } else {
  111. const lastTweetTimestamp = tweets[tweets.length - 1].created_at;
  112. intervalEnd = new Date(lastTweetTimestamp);
  113. }
  114. // Fetch private tweet hashs from P2P DB for corresponding interval
  115. const privateTweetHashs: string[] = await this.gun.fetchPrivateTweetHashsForUserInInterval(
  116. userId,
  117. new Date(),
  118. intervalEnd
  119. );
  120. if (privateTweetHashs.length) {
  121. // Load private tweets from P2P storage
  122. let privateTweets = await this.ipfs.fetchTweets(privateTweetHashs);
  123. // Add user object to private tweets
  124. privateTweets = await Promise.all(
  125. privateTweets.map(async tweet => await this.addUserToTweet(tweet))
  126. );
  127. // Combine and sort tweets
  128. return tweets
  129. .concat(privateTweets)
  130. .sort((a, b) => this.sortByDateAsc(a, b));
  131. } else {
  132. return tweets;
  133. }
  134. }
  135. private sortByDateAsc(a, b) {
  136. const dateA = new Date(a.created_at);
  137. const dateB = new Date(b.created_at);
  138. if (dateA > dateB) {
  139. return -1;
  140. } else if (dateA < dateB) {
  141. return 1;
  142. } else {
  143. return 0;
  144. }
  145. }
  146. private async addUserToTweet(tweet) {
  147. tweet.user = await this.twitter.fetchUser(tweet.user_id);
  148. return tweet;
  149. }
  150. onScroll(event) {
  151. this.enableRefresh = event.scrollTop === 0;
  152. }
  153. }