write-tweet.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. import { Component } from "@angular/core";
  2. import {
  3. IonicPage,
  4. NavController,
  5. NavParams,
  6. LoadingController,
  7. AlertController
  8. } from "ionic-angular";
  9. import { FormBuilder, Validators, FormGroup } from "@angular/forms";
  10. import { TwitterApiProvider } from "../../providers/twitter-api/twitter-api";
  11. import { Storage } from "@ionic/storage";
  12. import { P2pStorageIpfsProvider } from "../../providers/p2p-storage-ipfs/p2p-storage-ipfs";
  13. import { P2pDatabaseGunProvider } from "../../providers/p2p-database-gun/p2p-database-gun";
  14. import twittertext from "twitter-text";
  15. import { CryptoProvider } from "../../providers/crypto/crypto";
  16. @IonicPage()
  17. @Component({
  18. selector: "page-write-tweet",
  19. templateUrl: "write-tweet.html"
  20. })
  21. export class WriteTweetPage {
  22. tweet: FormGroup;
  23. retweetId: string;
  24. replyToStatusId: string;
  25. retweet;
  26. replyTweet;
  27. constructor(
  28. public navCtrl: NavController,
  29. public navParams: NavParams,
  30. private formBuilder: FormBuilder,
  31. private twitter: TwitterApiProvider,
  32. private loadingCtrl: LoadingController,
  33. private storage: Storage,
  34. private ipfs: P2pStorageIpfsProvider,
  35. private gun: P2pDatabaseGunProvider,
  36. private cryptoUtils: CryptoProvider,
  37. private alertCtrl: AlertController
  38. ) {
  39. this.retweetId = this.navParams.get("tweetId");
  40. this.replyToStatusId = this.navParams.get("replyToStatus");
  41. this.tweet = this.formBuilder.group({
  42. text: ["", Validators.maxLength(140)],
  43. p2p: [false]
  44. });
  45. }
  46. async ionViewDidLoad() {
  47. if (this.retweetId) {
  48. this.retweet = await this.twitter.fetchTweet(this.retweetId);
  49. }
  50. if (this.replyToStatusId) {
  51. this.replyTweet = await this.twitter.fetchTweet(this.replyToStatusId);
  52. }
  53. }
  54. get tweetCharProgress() {
  55. let progress = 1 - this.tweet.value["text"].length / 140;
  56. let radius = 8;
  57. let circumference = Math.PI * radius * 2;
  58. return progress * circumference;
  59. }
  60. async submitTweet() {
  61. const loading = this.loadingCtrl.create();
  62. loading.present();
  63. if (this.tweet.value.p2p) {
  64. loading.setContent("Validate keys...");
  65. if (
  66. (await this.cryptoUtils.isPrivateKeySet()) &&
  67. (await this.cryptoUtils.isPublicKeyPublished())
  68. ) {
  69. loading.setContent("Publish private tweet...");
  70. await this.tweetPrivate();
  71. } else {
  72. loading.dismiss();
  73. const alert = this.alertCtrl.create({
  74. title: "Oooops...",
  75. message:
  76. "Please verify that you have set a private and public key in the settings and that your latest public key was published."
  77. });
  78. alert.present();
  79. return;
  80. }
  81. } else {
  82. loading.setContent("Publish on Twitter...");
  83. await this.twitter.tweet(
  84. this.tweet.value["text"],
  85. this.retweet,
  86. this.replyToStatusId
  87. );
  88. }
  89. loading.dismiss();
  90. this.navCtrl.pop();
  91. }
  92. private async tweetPrivate() {
  93. const tweet = await this.buildPrivateTweet();
  94. const privateKey = await this.storage.get("privateKey");
  95. const encryptedTweet = this.cryptoUtils.encrypt(
  96. JSON.stringify(tweet),
  97. privateKey
  98. );
  99. const res = await this.ipfs.storeTweet(encryptedTweet);
  100. this.gun.storeLastTweetHashForUser(
  101. tweet.user_id,
  102. res["Hash"],
  103. tweet.created_at
  104. );
  105. this.gun.publishHashtags(tweet.entities.hashtags);
  106. }
  107. private async buildPrivateTweet() {
  108. const status = this.tweet.value["text"].trim();
  109. const entities = await this.getEntities(status);
  110. return {
  111. full_text: status,
  112. user_id: await this.storage.get("userId"),
  113. created_at: Date.now(),
  114. private_tweet: true,
  115. in_reply_to_status_id: this.replyToStatusId,
  116. quoted_status_id: this.retweetId,
  117. display_text_range: [0, status.length],
  118. entities: entities
  119. };
  120. }
  121. private async getEntities(status: string) {
  122. return {
  123. hashtags: twittertext.extractHashtagsWithIndices(status),
  124. urls: twittertext.extractUrlsWithIndices(status),
  125. user_mentions: await this.getMentions(status)
  126. };
  127. }
  128. private async getMentions(status: string) {
  129. // extract mentions
  130. const entities = twittertext.extractMentionsWithIndices(status);
  131. // add user_id
  132. const entitiesWithPromises = entities.map(async mention => {
  133. try {
  134. const user = await this.twitter.fetchUserFromScreenName(
  135. mention.screenName
  136. );
  137. mention["id_str"] = user[0]["id_str"];
  138. mention["screen_name"] = mention.screenName;
  139. delete mention.screenName;
  140. } catch (err) {
  141. console.error(
  142. "There is no user signed up to twitter with username: " +
  143. mention.screenName
  144. );
  145. }
  146. return mention;
  147. });
  148. // filter for valid users and return
  149. return (await Promise.all(entitiesWithPromises)).filter(el =>
  150. el.hasOwnProperty("id_str")
  151. );
  152. }
  153. }