write-tweet.ts 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. import { Component } from "@angular/core";
  2. import {
  3. IonicPage,
  4. NavController,
  5. NavParams,
  6. LoadingController,
  7. AlertController
  8. } from "ionic-angular";
  9. import {
  10. FormBuilder,
  11. Validators,
  12. FormGroup,
  13. ValidatorFn,
  14. AbstractControl
  15. } from "@angular/forms";
  16. import { MockProvider } from "../../providers/mock-provider/mock-provider";
  17. import { TwitterApiProvider } from "../../providers/twitter-api/twitter-api";
  18. import { Storage } from "@ionic/storage";
  19. import { P2pStorageIpfsProvider } from "../../providers/p2p-storage-ipfs/p2p-storage-ipfs";
  20. import { P2pDatabaseGunProvider } from "../../providers/p2p-database-gun/p2p-database-gun";
  21. import twittertext from "twitter-text";
  22. import { CryptoProvider } from "../../providers/crypto/crypto";
  23. @IonicPage()
  24. @Component({
  25. selector: "page-write-tweet",
  26. templateUrl: "write-tweet.html"
  27. })
  28. export class WriteTweetPage {
  29. tweet: FormGroup;
  30. retweetId: string;
  31. replyToStatusId: string;
  32. retweet;
  33. replyTweet;
  34. constructor(
  35. public navCtrl: NavController,
  36. public navParams: NavParams,
  37. private formBuilder: FormBuilder,
  38. private twitter: TwitterApiProvider,
  39. private loadingCtrl: LoadingController,
  40. private storage: Storage,
  41. private ipfs: P2pStorageIpfsProvider,
  42. private gun: P2pDatabaseGunProvider,
  43. private cryptoUtils: CryptoProvider,
  44. private alertCtrl: AlertController,
  45. private mockProv: MockProvider
  46. ) {
  47. this.retweetId = this.navParams.get("tweetId");
  48. this.replyToStatusId = this.navParams.get("replyToStatus");
  49. console.log('retweeting now? retweetId:', this.retweetId, 'replyToStatusId: ', this.replyToStatusId);
  50. this.tweet = this.formBuilder.group({
  51. text: [""],
  52. p2p: [false]
  53. });
  54. this.addValidators();
  55. }
  56. private async addValidators() {
  57. const triggerWords = await this.storage.get("keywords");
  58. const validators = [
  59. Validators.maxLength(140),
  60. this.containsTriggerWord(triggerWords)
  61. ];
  62. this.tweet.controls["text"].setValidators(validators);
  63. }
  64. private containsTriggerWord(triggerWords: string): ValidatorFn {
  65. return (control: AbstractControl): {
  66. [key: string]: any
  67. } | null => {
  68. if (triggerWords) {
  69. const regexList = triggerWords
  70. .toLowerCase()
  71. .split(", ")
  72. .join("|");
  73. const regex = new RegExp(regexList);
  74. const containsTriggerWord = regex.test(control.value.toLowerCase());
  75. return containsTriggerWord ? { containsTriggerWord: { value: control.value } } :
  76. null;
  77. } else {
  78. return null;
  79. }
  80. };
  81. }
  82. async ionViewDidLoad() {
  83. if (this.retweetId) {
  84. if (this.storage.get("mockup")) {
  85. this.retweet = this.mockProv.fetchTweet(this.retweetId);
  86. console.log("retweeted tweet", this.retweet);
  87. } else {
  88. this.retweet = await this.twitter.fetchTweet(this.retweetId);
  89. console.log('this.retweet is:', this.retweet);
  90. }
  91. }
  92. if (this.replyToStatusId) {
  93. if (this.storage.get("mockup"))
  94. console.log("replying in mockup");
  95. else
  96. this.replyTweet = await this.twitter.fetchTweet(this.replyToStatusId);
  97. console.log('this.replyTweet is:', this.replyTweet);
  98. }
  99. }
  100. get tweetCharProgress() {
  101. const progress = 1 - this.tweet.value["text"].length / 140;
  102. const radius = 8;
  103. const circumference = Math.PI * radius * 2;
  104. return progress * circumference;
  105. }
  106. get showTrigger(): boolean {
  107. return (
  108. this.tweet &&
  109. this.tweet.controls &&
  110. this.tweet.controls.text &&
  111. this.tweet.controls.text.errors &&
  112. this.tweet.controls.text.errors["containsTriggerWord"] &&
  113. !this.tweet.controls.p2p.value
  114. );
  115. }
  116. showTriggerInfo() {
  117. this.alertCtrl
  118. .create({
  119. title: "Watch Out!",
  120. message: "Your tweet contains words you have previously defined to only share securely via P2P. Currently P2P mode is not selected.",
  121. buttons: ["OK"]
  122. })
  123. .present();
  124. }
  125. async submitTweet() {
  126. console.log('submitting tweet');
  127. const loading = this.loadingCtrl.create();
  128. loading.present();
  129. console.log("storage mockup value: ", this.storage.get("mockup"));
  130. if (await this.storage.get("mockup")) {
  131. console.log('value of the tweet is:', this.tweet.value);
  132. console.log('publishing tweet for mock', this.tweet.value["text"]);
  133. await this.mockProv.writeTweet(this.tweet.value["text"],
  134. this.tweet.value.p2p,
  135. this.retweet,
  136. this.replyToStatusId);
  137. // loading.dismiss();
  138. } else {
  139. console.log('in else ');
  140. if (this.tweet.value.p2p) {
  141. loading.setContent("Validate keys...");
  142. if (
  143. (await this.cryptoUtils.isPrivateKeySet()) &&
  144. (await this.cryptoUtils.isPublicKeyPublished())
  145. ) {
  146. loading.setContent("Publish private tweet...");
  147. await this.tweetPrivate();
  148. } else {
  149. loading.dismiss();
  150. const alert = this.alertCtrl.create({
  151. title: "Oooops...",
  152. message: "Please verify that you have set a private and public key in the settings and that your latest public key was published."
  153. });
  154. alert.present();
  155. return;
  156. }
  157. } else {
  158. loading.setContent("Publish on Twitter...");
  159. await this.twitter.tweet(
  160. this.tweet.value["text"],
  161. this.retweet,
  162. this.replyToStatusId
  163. );
  164. }
  165. }
  166. loading.dismiss();
  167. this.navCtrl.pop();
  168. }
  169. private async tweetPrivate() {
  170. const tweet = await this.buildPrivateTweet();
  171. const privateKey = await this.storage.get("privateKey");
  172. const encryptedTweet = this.cryptoUtils.encrypt(
  173. JSON.stringify(tweet),
  174. privateKey
  175. );
  176. const res = await this.ipfs.storeTweet(encryptedTweet);
  177. this.gun.storeLastTweetHashForUser(
  178. tweet.user_id,
  179. res["Hash"],
  180. tweet.created_at
  181. );
  182. this.gun.publishHashtags(tweet.entities.hashtags);
  183. }
  184. private async buildPrivateTweet() {
  185. console.log('building private tweet');
  186. const status = this.tweet.value["text"].trim();
  187. const entities = await this.getEntities(status);
  188. return {
  189. full_text: status,
  190. user_id: await this.storage.get("userId"),
  191. created_at: Date.now(),
  192. private_tweet: true,
  193. in_reply_to_status_id: this.replyToStatusId,
  194. quoted_status_id: this.retweetId,
  195. display_text_range: [0, status.length],
  196. entities: entities
  197. };
  198. }
  199. private async getEntities(status: string) {
  200. return {
  201. hashtags: twittertext.extractHashtagsWithIndices(status),
  202. urls: twittertext.extractUrlsWithIndices(status),
  203. user_mentions: await this.getMentions(status)
  204. };
  205. }
  206. private async getMentions(status: string) {
  207. // extract mentions
  208. const entities = twittertext.extractMentionsWithIndices(status);
  209. // add user_id
  210. const entitiesWithPromises = entities.map(async mention => {
  211. try {
  212. const user = await this.twitter.fetchUserFromScreenName(
  213. mention.screenName
  214. );
  215. mention["id_str"] = user[0]["id_str"];
  216. mention["screen_name"] = mention.screenName;
  217. delete mention.screenName;
  218. } catch (err) {
  219. console.error(
  220. "There is no user signed up to twitter with username: " +
  221. mention.screenName
  222. );
  223. }
  224. return mention;
  225. });
  226. // filter for valid users and return
  227. return (await Promise.all(entitiesWithPromises)).filter(el =>
  228. el.hasOwnProperty("id_str")
  229. );
  230. }
  231. }