diff --git a/__tests__/__snapshots__/Storyshots.test.js.snap b/__tests__/__snapshots__/Storyshots.test.js.snap index 52cde22be..68b171855 100644 --- a/__tests__/__snapshots__/Storyshots.test.js.snap +++ b/__tests__/__snapshots__/Storyshots.test.js.snap @@ -44122,46 +44122,119 @@ exports[`Storyshots Room Item Alerts 1`] = ` > - + + + + +  + + + Read + + + + + - @@ -44170,7 +44243,7 @@ exports[`Storyshots Room Item Alerts 1`] = ` style={ Array [ Object { - "color": "white", + "color": "#ffffff", "fontSize": 20, }, undefined, @@ -44183,7 +44256,7 @@ exports[`Storyshots Room Item Alerts 1`] = ` ] } > -  +  - Read + Favorite - - - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - - - - - - - - -  - - - rocket.cat - - - - - - - - - @@ -44558,7 +44300,7 @@ exports[`Storyshots Room Item Alerts 1`] = ` style={ Array [ Object { - "color": "white", + "color": "#ffffff", "fontSize": 20, }, undefined, @@ -44571,7 +44313,7 @@ exports[`Storyshots Room Item Alerts 1`] = ` ] } > -  +  - Read + Hide - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - + > + + - - - -  - - - unread - +  + + - 1 + rocket.cat @@ -44939,46 +44512,119 @@ exports[`Storyshots Room Item Alerts 1`] = ` - + + + + +  + + + Read + + + + + - @@ -44987,7 +44633,7 @@ exports[`Storyshots Room Item Alerts 1`] = ` style={ Array [ Object { - "color": "white", + "color": "#ffffff", "fontSize": 20, }, undefined, @@ -45000,7 +44646,7 @@ exports[`Storyshots Room Item Alerts 1`] = ` ] } > -  +  - Read + Favorite + + + + +  + + + Hide - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - + > + + - - - -  - - - unread - +  + + - +999 + unread + + + 1 + + @@ -45368,46 +44943,119 @@ exports[`Storyshots Room Item Alerts 1`] = ` - + + + + +  + + + Read + + + + + - @@ -45416,7 +45064,7 @@ exports[`Storyshots Room Item Alerts 1`] = ` style={ Array [ Object { - "color": "white", + "color": "#ffffff", "fontSize": 20, }, undefined, @@ -45429,7 +45077,7 @@ exports[`Storyshots Room Item Alerts 1`] = ` ] } > -  +  - Read + Favorite + + + + +  + + + Hide - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - + > + + - - - -  - - - user mentions - +  + + - 1 + unread + + + +999 + + @@ -45797,46 +45374,119 @@ exports[`Storyshots Room Item Alerts 1`] = ` - + + + + +  + + + Read + + + + + - @@ -45845,7 +45495,7 @@ exports[`Storyshots Room Item Alerts 1`] = ` style={ Array [ Object { - "color": "white", + "color": "#ffffff", "fontSize": 20, }, undefined, @@ -45858,7 +45508,7 @@ exports[`Storyshots Room Item Alerts 1`] = ` ] } > -  +  - Read + Favorite + + + + +  + + + Hide - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - + > + + - - - -  - - - group mentions - +  + + - 1 + user mentions + + + 1 + + @@ -46226,46 +45805,119 @@ exports[`Storyshots Room Item Alerts 1`] = ` - + + + + +  + + + Read + + + + + - @@ -46274,7 +45926,7 @@ exports[`Storyshots Room Item Alerts 1`] = ` style={ Array [ Object { - "color": "white", + "color": "#ffffff", "fontSize": 20, }, undefined, @@ -46287,7 +45939,7 @@ exports[`Storyshots Room Item Alerts 1`] = ` ] } > -  +  - Read + Favorite + + + + +  + + + Hide - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - + > + + - - - -  - - - thread unread - +  + + - 1 + group mentions + + + 1 + + @@ -46655,46 +46236,119 @@ exports[`Storyshots Room Item Alerts 1`] = ` - + + + + +  + + + Read + + + + + - @@ -46703,7 +46357,7 @@ exports[`Storyshots Room Item Alerts 1`] = ` style={ Array [ Object { - "color": "white", + "color": "#ffffff", "fontSize": 20, }, undefined, @@ -46716,7 +46370,7 @@ exports[`Storyshots Room Item Alerts 1`] = ` ] } > -  +  - Read + Favorite + + + + +  + + + Hide - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - + > + + - - - -  - - - thread unread user - +  + + - 1 + thread unread + + + 1 + + @@ -47084,46 +46667,119 @@ exports[`Storyshots Room Item Alerts 1`] = ` - + + + + +  + + + Read + + + + + - @@ -47132,7 +46788,7 @@ exports[`Storyshots Room Item Alerts 1`] = ` style={ Array [ Object { - "color": "white", + "color": "#ffffff", "fontSize": 20, }, undefined, @@ -47145,7 +46801,7 @@ exports[`Storyshots Room Item Alerts 1`] = ` ] } > -  +  - Read + Favorite + + + + +  + + + Hide - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - + > + + - - - -  - - - thread unread group - +  + + - 1 + thread unread user + + + 1 + + @@ -47513,46 +47098,119 @@ exports[`Storyshots Room Item Alerts 1`] = ` - + + + + +  + + + Read + + + + + - @@ -47561,7 +47219,7 @@ exports[`Storyshots Room Item Alerts 1`] = ` style={ Array [ Object { - "color": "white", + "color": "#ffffff", "fontSize": 20, }, undefined, @@ -47574,7 +47232,7 @@ exports[`Storyshots Room Item Alerts 1`] = ` ] } > -  +  - Read + Favorite + + + + +  + + + Hide - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - + > + + - - - -  - - - user mentions priority 1 - +  + + - 1 + thread unread group + + + 1 + + @@ -47942,46 +47529,119 @@ exports[`Storyshots Room Item Alerts 1`] = ` - + + + + +  + + + Read + + + + + - @@ -47990,7 +47650,7 @@ exports[`Storyshots Room Item Alerts 1`] = ` style={ Array [ Object { - "color": "white", + "color": "#ffffff", "fontSize": 20, }, undefined, @@ -48003,7 +47663,7 @@ exports[`Storyshots Room Item Alerts 1`] = ` ] } > -  +  - Read + Favorite + + + + +  + + + Hide - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - + > + + - - - -  - - - group mentions priority 2 - +  + + - 1 + user mentions priority 1 + + + 1 + + @@ -48371,46 +47960,119 @@ exports[`Storyshots Room Item Alerts 1`] = ` - + + + + +  + + + Read + + + + + - @@ -48419,7 +48081,7 @@ exports[`Storyshots Room Item Alerts 1`] = ` style={ Array [ Object { - "color": "white", + "color": "#ffffff", "fontSize": 20, }, undefined, @@ -48432,7 +48094,7 @@ exports[`Storyshots Room Item Alerts 1`] = ` ] } > -  +  - Read + Favorite + + + + +  + + + Hide - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - + > + + - - + + +  + + + group mentions priority 2 + + + + 1 + + + + + + + + + + + + + -  +  - thread unread priority 3 + Read + + + + + + +  + + + Favorite + + + + +  + + + Hide + + + + + + + + + + + +  + + - 1 + thread unread priority 3 + + + 1 + + @@ -48813,46 +48835,119 @@ exports[`Storyshots Room Item Basic 1`] = ` > - + + + + +  + + + Read + + + + + - @@ -48861,7 +48956,7 @@ exports[`Storyshots Room Item Basic 1`] = ` style={ Array [ Object { - "color": "white", + "color": "#ffffff", "fontSize": 20, }, undefined, @@ -48874,7 +48969,7 @@ exports[`Storyshots Room Item Basic 1`] = ` ] } > -  +  - Read + Favorite + + + + +  + + + Hide - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - + > + + - - - + -  - - - rocket.cat - + Object { + "fontFamily": "custom", + "fontStyle": "normal", + "fontWeight": "normal", + }, + Object {}, + ] + } + > +  + + + rocket.cat + + @@ -49209,312 +49233,47 @@ exports[`Storyshots Room Item Last Message 1`] = ` > - + - -  - - - Read - - - - - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - - - - - - @@ -49523,30 +49282,10 @@ exports[`Storyshots Room Item Last Message 1`] = ` style={ Array [ Object { - "color": "#cbced1", - "fontSize": 16, + "color": "white", + "fontSize": 20, }, - Array [ - Object { - "height": 16, - "textAlignVertical": "center", - "width": 16, - }, - Array [ - Array [ - Object { - "marginRight": 4, - }, - Object { - "color": "#0d0e12", - }, - undefined, - ], - Object { - "color": "#cbced1", - }, - ], - ], + undefined, Object { "fontFamily": "custom", "fontStyle": "normal", @@ -49556,1899 +49295,319 @@ exports[`Storyshots Room Item Last Message 1`] = ` ] } > -  +  - rocket.cat - - - 10:00 - - - - - No Message - - - - - - - - - - - -  - - - Read - - - - - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - - - - - - - - -  - - - rocket.cat - - - 10:00 - - - - - 2 - - - - - - - - - - - -  - - - Read - - - - - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - - - - - - - - -  - - - rocket.cat - - - 10:00 - - - - - You: 1 - - - - - - - - - - - -  - - - Read - - - - - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - - - - - - - - -  - - - rocket.cat - - - 10:00 - - - - - Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries - - - - - - - - - - - -  - - - Read - - - - - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - - - - - - - - -  - - - rocket.cat - - - 10:00 + Read - + + + + +  + + + Favorite + + + + +  + + + Hide + + + + + + - - Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries - + + + + + +  + + + rocket.cat + + + 10:00 + + + + - 1 + No Message @@ -51475,46 +49677,119 @@ exports[`Storyshots Room Item Last Message 1`] = ` - + + + + +  + + + Read + + + + + - @@ -51523,7 +49798,7 @@ exports[`Storyshots Room Item Last Message 1`] = ` style={ Array [ Object { - "color": "white", + "color": "#ffffff", "fontSize": 20, }, undefined, @@ -51536,7 +49811,7 @@ exports[`Storyshots Room Item Last Message 1`] = ` ] } > -  +  - Read + Favorite + + + + +  + + + Hide - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - - - - - - -  - - - rocket.cat - - - 10:00 - - - - - Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries - + + + + + +  + + + rocket.cat + + + 10:00 + + + + - +999 + 2 @@ -51973,46 +50121,119 @@ exports[`Storyshots Room Item Last Message 1`] = ` - + + + + +  + + + Read + + + + + - @@ -52021,7 +50242,7 @@ exports[`Storyshots Room Item Last Message 1`] = ` style={ Array [ Object { - "color": "white", + "color": "#ffffff", "fontSize": 20, }, undefined, @@ -52034,7 +50255,7 @@ exports[`Storyshots Room Item Last Message 1`] = ` ] } > -  +  - Read + Favorite + + + + +  + + + Hide - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - + + + + + + > + +  + + + rocket.cat + + + 10:00 + + + + + You: 1 + + + + + + + + @@ -52287,30 +50614,10 @@ exports[`Storyshots Room Item Last Message 1`] = ` style={ Array [ Object { - "color": "#cbced1", - "fontSize": 16, + "color": "white", + "fontSize": 20, }, - Array [ - Object { - "height": 16, - "textAlignVertical": "center", - "width": 16, - }, - Array [ - Array [ - Object { - "marginRight": 4, - }, - Object { - "color": "#0d0e12", - }, - undefined, - ], - Object { - "color": "#cbced1", - }, - ], - ], + undefined, Object { "fontFamily": "custom", "fontStyle": "normal", @@ -52320,131 +50627,319 @@ exports[`Storyshots Room Item Last Message 1`] = ` ] } > -  +  - rocket.cat - - - 10:00 + Read - + + + + +  + + + Favorite + + + + +  + + + Hide + + + + + + - - Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries - + + + + + +  + + + rocket.cat + + + 10:00 + + + + - 1 + Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries + + + + + + + + + + + + + +  + + + Read + + + + + + + +  + + + Favorite + + + + +  + + + Hide + + + + + + + + + + + + + +  + + + rocket.cat + + + 10:00 + + + + + Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries + + + + 1 + + + + + + + + + + + + + + +  + + + Read + + + + + + + +  + + + Favorite + + + + +  + + + Hide + + + + + + + + + + + + + +  + + + rocket.cat + + + 10:00 + + + + + Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries + + + + +999 + + + + + + + + + + + + + + +  + + + Read + + + + + + + +  + + + Favorite + + + + +  + + + Hide + + + + + + + + + + + + + +  + + + rocket.cat + + + 10:00 + + + + + Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries + + + + 1 + + + + + + + + + + +`; + +exports[`Storyshots Room Item Tag 1`] = ` + + + + + + + + +  + + + Read + + + + + + + +  + + + Favorite + + + + +  + + + Hide + + + + + + + + + + + + + +  + + + rocket.cat + + + + Auto-join + + + + + + + + + + + + + + +  + + + Read + + + + + + + +  + + + Favorite + + + + +  + + + Hide + + + + + + + + + + + + + +  + + + rocket.cat + + + + Auto-join + + + + 10:00 + + + + + No Message + + + + + + + + + + + + + +  + + + Read + + + + + + + +  + + + Favorite + + + + +  + + + Hide + + + + + + + + + + + + + +  + + + Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries + + + + Auto-join + + + + + + + + + + + + + + +  + + + Read + + + + + + + +  + + + Favorite + + + + +  + + + Hide + + + + + + + + + + + + + +  + + + Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries + + + + Auto-join + + + + 10:00 + + + + + No Message @@ -52484,46 +54337,119 @@ exports[`Storyshots Room Item Type 1`] = ` > - + + + + +  + + + Read + + + + + - @@ -52532,7 +54458,7 @@ exports[`Storyshots Room Item Type 1`] = ` style={ Array [ Object { - "color": "white", + "color": "#ffffff", "fontSize": 20, }, undefined, @@ -52545,7 +54471,7 @@ exports[`Storyshots Room Item Type 1`] = ` ] } > -  +  - Read + Favorite + + + + +  + + + Hide - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - + > + + - - - + +  + + + rocket.cat + + + + + + + + + + + + + -  +  - rocket.cat + Read - - - - - @@ -52915,7 +54843,7 @@ exports[`Storyshots Room Item Type 1`] = ` style={ Array [ Object { - "color": "white", + "color": "#ffffff", "fontSize": 20, }, undefined, @@ -52928,7 +54856,7 @@ exports[`Storyshots Room Item Type 1`] = ` ] } > -  +  - Read + Favorite - - - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - - - - - - - - -  - - - rocket.cat - - - - - - - - - @@ -53286,7 +54900,7 @@ exports[`Storyshots Room Item Type 1`] = ` style={ Array [ Object { - "color": "white", + "color": "#ffffff", "fontSize": 20, }, undefined, @@ -53299,7 +54913,7 @@ exports[`Storyshots Room Item Type 1`] = ` ] } > -  +  - Read + Hide - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - + > + + - - + + +  + + + rocket.cat + + + + + + + + + + + + -  +  - rocket.cat + Read - - - - - @@ -53657,7 +55216,7 @@ exports[`Storyshots Room Item Type 1`] = ` style={ Array [ Object { - "color": "white", + "color": "#ffffff", "fontSize": 20, }, undefined, @@ -53670,7 +55229,7 @@ exports[`Storyshots Room Item Type 1`] = ` ] } > -  +  - Read + Favorite - - - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - - - - - - - - -  - - - rocket.cat - - - - - - - - - @@ -54028,7 +55273,7 @@ exports[`Storyshots Room Item Type 1`] = ` style={ Array [ Object { - "color": "white", + "color": "#ffffff", "fontSize": 20, }, undefined, @@ -54041,7 +55286,7 @@ exports[`Storyshots Room Item Type 1`] = ` ] } > -  +  - Read + Hide - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - + > + + - - + + +  + + + rocket.cat + + + + + + + + + + + + -  +  - rocket.cat + Read - - - - - @@ -54399,7 +55589,7 @@ exports[`Storyshots Room Item Type 1`] = ` style={ Array [ Object { - "color": "white", + "color": "#ffffff", "fontSize": 20, }, undefined, @@ -54412,7 +55602,7 @@ exports[`Storyshots Room Item Type 1`] = ` ] } > -  +  - Read + Favorite - - - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - - - - - - - - -  - - - rocket.cat - - - - - - - - - @@ -54770,7 +55646,7 @@ exports[`Storyshots Room Item Type 1`] = ` style={ Array [ Object { - "color": "white", + "color": "#ffffff", "fontSize": 20, }, undefined, @@ -54783,7 +55659,7 @@ exports[`Storyshots Room Item Type 1`] = ` ] } > -  +  - Read + Hide - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - + > + + - - + + +  + + + rocket.cat + + + + + + + + + + + + -  +  - rocket.cat + Read + + + +  + + + Favorite + + + + +  + + + Hide + + + + + + + + + + + + + +  + + + rocket.cat + + + + + + + + + + + + + +  + + + Read + + + + + + + +  + + + Favorite + + + + +  + + + Hide + + + + + + + + + + + + + +  + + + rocket.cat + + + + + + + + + + + + + +  + + + Read + + + + + + + +  + + + Favorite + + + + +  + + + Hide + + + + + + + + + + + + + +  + + + rocket.cat + + + + + @@ -55106,46 +56973,119 @@ exports[`Storyshots Room Item User 1`] = ` > - + + + + +  + + + Read + + + + + - @@ -55154,7 +57094,7 @@ exports[`Storyshots Room Item User 1`] = ` style={ Array [ Object { - "color": "white", + "color": "#ffffff", "fontSize": 20, }, undefined, @@ -55167,7 +57107,7 @@ exports[`Storyshots Room Item User 1`] = ` ] } > -  +  - Read + Favorite + + + + +  + + + Hide - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - + > + + - - - + -  - - - diego.mello - + Object { + "fontFamily": "custom", + "fontStyle": "normal", + "fontWeight": "normal", + }, + Object {}, + ] + } + > +  + + + diego.mello + + - + + + + +  + + + Read + + + + + - @@ -55537,7 +57479,7 @@ exports[`Storyshots Room Item User 1`] = ` style={ Array [ Object { - "color": "white", + "color": "#ffffff", "fontSize": 20, }, undefined, @@ -55550,7 +57492,7 @@ exports[`Storyshots Room Item User 1`] = ` ] } > -  +  - Read + Favorite + + + + +  + + + Hide - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - + > + + - - - + -  - - - Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries - + Object { + "fontFamily": "custom", + "fontStyle": "normal", + "fontWeight": "normal", + }, + Object {}, + ] + } + > +  + + + Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries + + @@ -55885,46 +57756,119 @@ exports[`Storyshots Room Item User status 1`] = ` > - + + + + +  + + + Read + + + + + - @@ -55933,7 +57877,7 @@ exports[`Storyshots Room Item User status 1`] = ` style={ Array [ Object { - "color": "white", + "color": "#ffffff", "fontSize": 20, }, undefined, @@ -55946,7 +57890,7 @@ exports[`Storyshots Room Item User status 1`] = ` ] } > -  +  - Read + Favorite + + + + +  + + + Hide - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - + > + + - - - + -  - - - rocket.cat - + Object { + "fontFamily": "custom", + "fontStyle": "normal", + "fontWeight": "normal", + }, + Object {}, + ] + } + > +  + + + rocket.cat + + - + + + + +  + + + Read + + + + + - @@ -56316,7 +58262,7 @@ exports[`Storyshots Room Item User status 1`] = ` style={ Array [ Object { - "color": "white", + "color": "#ffffff", "fontSize": 20, }, undefined, @@ -56329,7 +58275,7 @@ exports[`Storyshots Room Item User status 1`] = ` ] } > -  +  - Read + Favorite + + + + +  + + + Hide - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - + > + + - - - + -  - - - rocket.cat - + Object { + "fontFamily": "custom", + "fontStyle": "normal", + "fontWeight": "normal", + }, + Object {}, + ] + } + > +  + + + rocket.cat + + - + + + + +  + + + Read + + + + + - @@ -56699,7 +58647,7 @@ exports[`Storyshots Room Item User status 1`] = ` style={ Array [ Object { - "color": "white", + "color": "#ffffff", "fontSize": 20, }, undefined, @@ -56712,7 +58660,7 @@ exports[`Storyshots Room Item User status 1`] = ` ] } > -  +  - Read + Favorite + + + + +  + + + Hide - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - + > + + - - - + -  - - - rocket.cat - + Object { + "fontFamily": "custom", + "fontStyle": "normal", + "fontWeight": "normal", + }, + Object {}, + ] + } + > +  + + + rocket.cat + + - + + + + +  + + + Read + + + + + - @@ -57082,7 +59032,7 @@ exports[`Storyshots Room Item User status 1`] = ` style={ Array [ Object { - "color": "white", + "color": "#ffffff", "fontSize": 20, }, undefined, @@ -57095,7 +59045,7 @@ exports[`Storyshots Room Item User status 1`] = ` ] } > -  +  - Read + Favorite + + + + +  + + + Hide - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - + > + + - - - + -  - - - rocket.cat - + Object { + "fontFamily": "custom", + "fontStyle": "normal", + "fontWeight": "normal", + }, + Object {}, + ] + } + > +  + + + rocket.cat + + - + + + + +  + + + Read + + + + + - @@ -57465,7 +59417,7 @@ exports[`Storyshots Room Item User status 1`] = ` style={ Array [ Object { - "color": "white", + "color": "#ffffff", "fontSize": 20, }, undefined, @@ -57478,7 +59430,7 @@ exports[`Storyshots Room Item User status 1`] = ` ] } > -  +  - Read + Favorite + + + + +  + + + Hide - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - + > + + - - - + -  - - - rocket.cat - + Object { + "fontFamily": "custom", + "fontStyle": "normal", + "fontWeight": "normal", + }, + Object {}, + ] + } + > +  + + + rocket.cat + + - + + + + +  + + + Read + + + + + - @@ -57848,7 +59802,7 @@ exports[`Storyshots Room Item User status 1`] = ` style={ Array [ Object { - "color": "white", + "color": "#ffffff", "fontSize": 20, }, undefined, @@ -57861,7 +59815,7 @@ exports[`Storyshots Room Item User status 1`] = ` ] } > -  +  - Read + Favorite + + + + +  + + + Hide - - - -  - - - Favorite - - - - -  - - - Hide - - - - - - + > + + - - - + -  - - - rocket.cat - + Object { + "fontFamily": "custom", + "fontStyle": "normal", + "fontWeight": "normal", + }, + Object {}, + ] + } + > +  + + + rocket.cat + + diff --git a/app/containers/ActionSheet/Item.js b/app/containers/ActionSheet/Item.js index 7cd5e7b4d..aa76da8bb 100644 --- a/app/containers/ActionSheet/Item.js +++ b/app/containers/ActionSheet/Item.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Text } from 'react-native'; +import { Text, View } from 'react-native'; import { themes } from '../../constants/colors'; import { CustomIcon } from '../../lib/Icons'; @@ -20,12 +20,19 @@ export const Item = React.memo(({ item, hide, theme }) => { theme={theme} > - - {item.title} - + + + {item.title} + + + { item.right ? ( + + {item.right ? item.right() : null} + + ) : null } ); }); @@ -34,7 +41,8 @@ Item.propTypes = { title: PropTypes.string, icon: PropTypes.string, danger: PropTypes.bool, - onPress: PropTypes.func + onPress: PropTypes.func, + right: PropTypes.func }), hide: PropTypes.func, theme: PropTypes.string diff --git a/app/containers/ActionSheet/styles.js b/app/containers/ActionSheet/styles.js index 57fe0bc82..1b9397dc9 100644 --- a/app/containers/ActionSheet/styles.js +++ b/app/containers/ActionSheet/styles.js @@ -22,6 +22,9 @@ export default StyleSheet.create({ content: { paddingTop: 16 }, + titleContainer: { + flex: 1 + }, title: { fontSize: 16, marginLeft: 16, @@ -58,5 +61,8 @@ export default StyleSheet.create({ fontSize: 16, ...sharedStyles.textMedium, ...sharedStyles.textAlignCenter + }, + rightContainer: { + paddingLeft: 12 } }); diff --git a/app/containers/RoomTypeIcon.js b/app/containers/RoomTypeIcon.js index 55294310d..7c0e32c13 100644 --- a/app/containers/RoomTypeIcon.js +++ b/app/containers/RoomTypeIcon.js @@ -30,6 +30,7 @@ const RoomTypeIcon = React.memo(({ return ; } + // TODO: move this to a separate function let icon = 'channel-private'; if (teamMain) { icon = `teams${ type === 'p' ? '-private' : '' }`; diff --git a/app/i18n/locales/en.json b/app/i18n/locales/en.json index bd4e1f8a2..8cc6cb278 100644 --- a/app/i18n/locales/en.json +++ b/app/i18n/locales/en.json @@ -435,6 +435,7 @@ "Review_app_unable_store": "Unable to open {{store}}", "Review_this_app": "Review this app", "Remove": "Remove", + "remove": "remove", "Roles": "Roles", "Room_actions": "Room actions", "Room_changed_announcement": "Room announcement changed to: {{announcement}} by {{userBy}}", @@ -716,5 +717,14 @@ "Read_Only_Team": "Read Only Team", "Broadcast_Team": "Broadcast Team", "creating_team": "creating team", - "team-name-already-exists": "A team with that name already exists" -} \ No newline at end of file + "team-name-already-exists": "A team with that name already exists", + "Add_Channel_to_Team": "Add Channel to Team", + "Create_New": "Create New", + "Add_Existing": "Add Existing", + "Add_Existing_Channel": "Add Existing Channel", + "Remove_from_Team": "Remove from Team", + "Auto-join": "Auto-join", + "Delete_Team_Room_Warning": "Woud you like to remove this channel from the team? The channel will be moved back to the workspace", + "Confirmation": "Confirmation", + "invalid-room": "Invalid room" +} diff --git a/app/lib/methods/getPermissions.js b/app/lib/methods/getPermissions.js index 09b91aa63..1f4bcb9cb 100644 --- a/app/lib/methods/getPermissions.js +++ b/app/lib/methods/getPermissions.js @@ -13,6 +13,7 @@ const PERMISSIONS = [ 'add-user-to-any-c-room', 'add-user-to-any-p-room', 'add-user-to-joined-room', + 'add-team-channel', 'archive-room', 'auto-translate', 'create-invite-links', @@ -21,11 +22,13 @@ const PERMISSIONS = [ 'delete-p', 'edit-message', 'edit-room', + 'edit-team-channel', 'force-delete-message', 'mute-user', 'pin-message', 'post-readonly', 'remove-user', + 'remove-team-channel', 'set-leader', 'set-moderator', 'set-owner', @@ -38,7 +41,9 @@ const PERMISSIONS = [ 'view-privileged-setting', 'view-room-administration', 'view-statistics', - 'view-user-administration' + 'view-user-administration', + 'view-all-teams', + 'view-all-team-channels' ]; export async function setPermissions() { diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index adc7f80ff..57c6e0c71 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -95,10 +95,19 @@ const RocketChat = { }, canOpenRoom, createChannel({ - name, users, type, readOnly, broadcast, encrypted + name, users, type, readOnly, broadcast, encrypted, teamId }) { - // RC 0.51.0 - return this.methodCallWrapper(type ? 'createPrivateGroup' : 'createChannel', name, users, readOnly, {}, { broadcast, encrypted }); + const params = { + name, + members: users, + readOnly, + extraData: { + broadcast, + encrypted, + ...(teamId && { teamId }) + } + }; + return this.post(type ? 'groups.create' : 'channels.create', params); }, async getWebsocketInfo({ server }) { const sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl: useSsl(server) }); @@ -648,7 +657,8 @@ const RocketChat = { avatarETag: sub.avatarETag, t: sub.t, encrypted: sub.encrypted, - lastMessage: sub.lastMessage + lastMessage: sub.lastMessage, + ...(sub.teamId && { teamId: sub.teamId }) })); return data; @@ -751,6 +761,18 @@ const RocketChat = { // RC 3.13.0 return this.post('teams.create', params); }, + addRoomsToTeam({ teamId, rooms }) { + // RC 3.13.0 + return this.post('teams.addRooms', { teamId, rooms }); + }, + removeTeamRoom({ roomId, teamId }) { + // RC 3.13.0 + return this.post('teams.removeRoom', { roomId, teamId }); + }, + updateTeamRoom({ roomId, isDefault }) { + // RC 3.13.0 + return this.post('teams.updateRoom', { roomId, isDefault }); + }, joinRoom(roomId, joinCode, type) { // TODO: join code // RC 0.48.0 diff --git a/app/presentation/RoomItem/RoomItem.js b/app/presentation/RoomItem/RoomItem.js index b3922787b..065e91331 100644 --- a/app/presentation/RoomItem/RoomItem.js +++ b/app/presentation/RoomItem/RoomItem.js @@ -10,6 +10,8 @@ import LastMessage from './LastMessage'; import Title from './Title'; import UpdatedAt from './UpdatedAt'; import Touchable from './Touchable'; +import Tag from './Tag'; +import I18n from '../../i18n'; const RoomItem = ({ rid, @@ -42,13 +44,16 @@ const RoomItem = ({ testID, swipeEnabled, onPress, + onLongPress, toggleFav, toggleRead, hideChannel, - teamMain + teamMain, + autoJoin }) => ( + { + autoJoin ? : null + } + { + autoJoin ? : null + } { + const { theme } = useTheme(); + + return ( + + + {name} + + + ); +}); + +Tag.propTypes = { + name: PropTypes.string +}; + +export default Tag; diff --git a/app/presentation/RoomItem/Touchable.js b/app/presentation/RoomItem/Touchable.js index defb5e105..bbf7cbf86 100644 --- a/app/presentation/RoomItem/Touchable.js +++ b/app/presentation/RoomItem/Touchable.js @@ -1,7 +1,9 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Animated } from 'react-native'; -import { PanGestureHandler, State } from 'react-native-gesture-handler'; +import { + LongPressGestureHandler, PanGestureHandler, State +} from 'react-native-gesture-handler'; import Touch from '../../utils/touch'; import { @@ -17,6 +19,7 @@ class Touchable extends React.Component { static propTypes = { type: PropTypes.string.isRequired, onPress: PropTypes.func, + onLongPress: PropTypes.func, testID: PropTypes.string, width: PropTypes.number, favorite: PropTypes.bool, @@ -59,6 +62,12 @@ class Touchable extends React.Component { } } + onLongPressHandlerStateChange = ({ nativeEvent }) => { + if (nativeEvent.state === State.ACTIVE) { + this.onLongPress(); + } + } + _handleRelease = (nativeEvent) => { const { translationX } = nativeEvent; @@ -203,54 +212,70 @@ class Touchable extends React.Component { } }; + onLongPress = () => { + const { rowState } = this.state; + const { onLongPress } = this.props; + if (rowState !== 0) { + this.close(); + return; + } + + if (onLongPress) { + onLongPress(); + } + }; + render() { const { testID, isRead, width, favorite, children, theme, isFocused, swipeEnabled } = this.props; return ( - - + - - - - - {children} - - - + + + + + + {children} + + + - + + + ); } } diff --git a/app/presentation/RoomItem/index.js b/app/presentation/RoomItem/index.js index 80bcf063b..d56194f8b 100644 --- a/app/presentation/RoomItem/index.js +++ b/app/presentation/RoomItem/index.js @@ -16,7 +16,8 @@ const attrs = [ 'theme', 'isFocused', 'forceUpdate', - 'showLastMessage' + 'showLastMessage', + 'autoJoin' ]; class RoomItemContainer extends React.Component { @@ -25,6 +26,7 @@ class RoomItemContainer extends React.Component { showLastMessage: PropTypes.bool, id: PropTypes.string, onPress: PropTypes.func, + onLongPress: PropTypes.func, username: PropTypes.string, avatarSize: PropTypes.number, width: PropTypes.number, @@ -41,7 +43,8 @@ class RoomItemContainer extends React.Component { getRoomAvatar: PropTypes.func, getIsGroupChat: PropTypes.func, getIsRead: PropTypes.func, - swipeEnabled: PropTypes.bool + swipeEnabled: PropTypes.bool, + autoJoin: PropTypes.bool }; static defaultProps = { @@ -112,6 +115,11 @@ class RoomItemContainer extends React.Component { return onPress(item); } + onLongPress = () => { + const { item, onLongPress } = this.props; + return onLongPress(item); + } + render() { const { item, @@ -129,7 +137,8 @@ class RoomItemContainer extends React.Component { showLastMessage, username, useRealName, - swipeEnabled + swipeEnabled, + autoJoin } = this.props; const name = getRoomTitle(item); const testID = `rooms-list-view-item-${ name }`; @@ -160,6 +169,7 @@ class RoomItemContainer extends React.Component { isGroupChat={this.isGroupChat} isRead={isRead} onPress={this.onPress} + onLongPress={this.onLongPress} date={date} accessibilityLabel={accessibilityLabel} width={width} @@ -189,6 +199,7 @@ class RoomItemContainer extends React.Component { tunreadGroup={item.tunreadGroup} swipeEnabled={swipeEnabled} teamMain={item.teamMain} + autoJoin={autoJoin} /> ); } diff --git a/app/presentation/RoomItem/styles.js b/app/presentation/RoomItem/styles.js index 80bf0c90b..787546c76 100644 --- a/app/presentation/RoomItem/styles.js +++ b/app/presentation/RoomItem/styles.js @@ -96,5 +96,16 @@ export default StyleSheet.create({ height: '100%', alignItems: 'center', justifyContent: 'center' + }, + tagContainer: { + alignSelf: 'center', + alignItems: 'center', + borderRadius: 4, + marginHorizontal: 4 + }, + tagText: { + fontSize: 13, + paddingHorizontal: 4, + ...sharedStyles.textSemibold } }); diff --git a/app/sagas/createChannel.js b/app/sagas/createChannel.js index f2ecfe76d..8979e230e 100644 --- a/app/sagas/createChannel.js +++ b/app/sagas/createChannel.js @@ -40,18 +40,26 @@ const handleRequest = function* handleRequest({ data }) { broadcast, encrypted } = data; - logEvent(events.CR_CREATE, { + logEvent(events.CT_CREATE, { type, readOnly, broadcast, encrypted }); - sub = yield call(createTeam, data); + const result = yield call(createTeam, data); + sub = { + rid: result?.team?.roomId, + ...result.team, + t: result.team.type ? 'p' : 'c' + }; } else if (data.group) { logEvent(events.SELECTED_USERS_CREATE_GROUP); const result = yield call(createGroupChat); if (result.success) { - ({ room: sub } = result); + sub = { + rid: result.room?._id, + ...result.room + }; } } else { const { @@ -66,33 +74,26 @@ const handleRequest = function* handleRequest({ data }) { broadcast, encrypted }); - sub = yield call(createChannel, data); + const result = yield call(createChannel, data); + sub = { + rid: result?.channel?._id || result?.group?._id, + ...result?.channel, + ...result?.group + }; } - try { const db = database.active; const subCollection = db.get('subscriptions'); yield db.action(async() => { await subCollection.create((s) => { - s._raw = sanitizedRaw({ id: sub.team ? sub.team.roomId : sub.rid }, subCollection.schema); + s._raw = sanitizedRaw({ id: sub.rid }, subCollection.schema); Object.assign(s, sub); }); }); } catch { // do nothing } - - let successParams = {}; - if (data.isTeam) { - successParams = { - ...sub.team, - rid: sub.team.roomId, - t: sub.team.type ? 'p' : 'c' - }; - } else { - successParams = data; - } - yield put(createChannelSuccess(successParams)); + yield put(createChannelSuccess(sub)); } catch (err) { logEvent(events[data.group ? 'SELECTED_USERS_CREATE_GROUP_F' : 'CR_CREATE_F']); yield put(createChannelFailure(err)); diff --git a/app/stacks/InsideStack.js b/app/stacks/InsideStack.js index bda56f0d2..75758960b 100644 --- a/app/stacks/InsideStack.js +++ b/app/stacks/InsideStack.js @@ -71,6 +71,8 @@ import ShareView from '../views/ShareView'; import CreateDiscussionView from '../views/CreateDiscussionView'; import QueueListView from '../ee/omnichannel/views/QueueListView'; +import AddChannelTeamView from '../views/AddChannelTeamView'; +import AddExistingChannelView from '../views/AddExistingChannelView'; // ChatsStackNavigator const ChatsStack = createStackNavigator(); @@ -174,6 +176,16 @@ const ChatsStackNavigator = () => { component={TeamChannelsView} options={TeamChannelsView.navigationOptions} /> + + { component={InviteUsersView} options={InviteUsersView.navigationOptions} /> + + { + const options = { + headerTitle: I18n.t('Add_Channel_to_Team') + }; + + if (isMasterDetail) { + options.headerLeft = () => ; + } + + navigation.setOptions(options); +}; + +const AddChannelTeamView = ({ + navigation, route, isMasterDetail +}) => { + const { teamId, teamChannels } = route.params; + const { theme } = useTheme(); + + useEffect(() => { + setHeader(navigation, isMasterDetail); + }, []); + + return ( + + + + + navigation.navigate('NewMessageStackNavigator', { screen: 'SelectedUsersViewCreateChannel', params: { nextAction: () => navigation.navigate('CreateChannelView', { teamId }) } })} + testID='add-channel-team-view-create-channel' + left={() => } + right={() => } + theme={theme} + /> + + navigation.navigate('AddExistingChannelView', { teamId, teamChannels })} + testID='add-channel-team-view-create-channel' + left={() => } + right={() => } + theme={theme} + /> + + + + ); +}; + +AddChannelTeamView.propTypes = { + route: PropTypes.object, + navigation: PropTypes.object, + isMasterDetail: PropTypes.bool +}; + +const mapStateToProps = state => ({ + isMasterDetail: state.app.isMasterDetail +}); + +export default connect(mapStateToProps)(AddChannelTeamView); diff --git a/app/views/AddExistingChannelView.js b/app/views/AddExistingChannelView.js new file mode 100644 index 000000000..a3acf9e23 --- /dev/null +++ b/app/views/AddExistingChannelView.js @@ -0,0 +1,209 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { + View, FlatList +} from 'react-native'; +import { connect } from 'react-redux'; +import { Q } from '@nozbe/watermelondb'; + +import * as List from '../containers/List'; +import database from '../lib/database'; +import RocketChat from '../lib/rocketchat'; +import I18n from '../i18n'; +import log, { events, logEvent } from '../utils/log'; +import SearchBox from '../containers/SearchBox'; +import * as HeaderButton from '../containers/HeaderButton'; +import StatusBar from '../containers/StatusBar'; +import { themes } from '../constants/colors'; +import { withTheme } from '../theme'; +import SafeAreaView from '../containers/SafeAreaView'; +import { animateNextTransition } from '../utils/layoutAnimation'; +import { goRoom } from '../utils/goRoom'; +import Loading from '../containers/Loading'; + +const QUERY_SIZE = 50; + +class AddExistingChannelView extends React.Component { + static propTypes = { + navigation: PropTypes.object, + route: PropTypes.object, + theme: PropTypes.string, + isMasterDetail: PropTypes.bool, + addTeamChannelPermission: PropTypes.array + }; + + constructor(props) { + super(props); + this.init(); + this.teamId = props.route?.params?.teamId; + this.state = { + search: [], + channels: [], + selected: [], + loading: false + }; + this.setHeader(); + } + + setHeader = () => { + const { navigation, isMasterDetail } = this.props; + const { selected } = this.state; + + const options = { + headerTitle: I18n.t('Add_Existing_Channel') + }; + + if (isMasterDetail) { + options.headerLeft = () => ; + } + + options.headerRight = () => selected.length > 0 && ( + + + + ); + + navigation.setOptions(options); + } + + init = async() => { + try { + const { addTeamChannelPermission } = this.props; + const db = database.active; + const channels = await db.collections + .get('subscriptions') + .query( + Q.where('team_id', ''), + Q.where('t', Q.oneOf(['c', 'p'])), + Q.experimentalTake(QUERY_SIZE), + Q.experimentalSortBy('room_updated_at', Q.desc) + ) + .fetch(); + const filteredChannels = channels.filter(async(channel) => { + const permissions = await RocketChat.hasPermission([addTeamChannelPermission], channel.rid); + if (!permissions[0]) { + return; + } + return channel; + }); + this.setState({ channels: filteredChannels }); + } catch (e) { + log(e); + } + } + + onSearchChangeText(text) { + this.search(text); + } + + dismiss = () => { + const { navigation } = this.props; + return navigation.pop(); + } + + search = async(text) => { + const result = await RocketChat.search({ text, filterUsers: false }); + this.setState({ + search: result + }); + } + + submit = async() => { + const { selected } = this.state; + const { isMasterDetail } = this.props; + + this.setState({ loading: true }); + try { + logEvent(events.CT_ADD_ROOM_TO_TEAM); + const result = await RocketChat.addRoomsToTeam({ rooms: selected, teamId: this.teamId }); + if (result.success) { + this.setState({ loading: false }); + goRoom({ item: result, isMasterDetail }); + } + } catch (e) { + logEvent(events.CT_ADD_ROOM_TO_TEAM_F); + this.setState({ loading: false }); + } + } + + renderHeader = () => { + const { theme } = this.props; + return ( + + this.onSearchChangeText(text)} testID='add-existing-channel-view-search' /> + + ); + } + + isChecked = (rid) => { + const { selected } = this.state; + return selected.includes(rid); + } + + toggleChannel = (rid) => { + const { selected } = this.state; + + animateNextTransition(); + if (!this.isChecked(rid)) { + logEvent(events.EXISTING_CHANNEL_ADD_CHANNEL); + this.setState({ selected: [...selected, rid] }, () => this.setHeader()); + } else { + logEvent(events.EXISTING_CHANNEL_REMOVE_CHANNEL); + const filterSelected = selected.filter(el => el !== rid); + this.setState({ selected: filterSelected }, () => this.setHeader()); + } + } + + renderItem = ({ item }) => { + const isChecked = this.isChecked(item.rid); + // TODO: reuse logic inside RoomTypeIcon + const icon = item.t === 'p' && !item.teamId ? 'channel-private' : 'channel-public'; + return ( + this.toggleChannel(item.rid)} + testID='add-existing-channel-view-item' + left={() => } + right={() => (isChecked ? : null)} + /> + + ); + } + + renderList = () => { + const { search, channels } = this.state; + const { theme } = this.props; + return ( + 0 ? search : channels} + extraData={this.state} + keyExtractor={item => item._id} + ListHeaderComponent={this.renderHeader} + renderItem={this.renderItem} + ItemSeparatorComponent={List.Separator} + contentContainerStyle={{ backgroundColor: themes[theme].backgroundColor }} + keyboardShouldPersistTaps='always' + /> + ); + } + + render() { + const { loading } = this.state; + + return ( + + + {this.renderList()} + + + ); + } +} + +const mapStateToProps = state => ({ + isMasterDetail: state.app.isMasterDetail, + addTeamChannelPermission: state.permissions['add-team-channel'] +}); + +export default connect(mapStateToProps)(withTheme(AddExistingChannelView)); diff --git a/app/views/CreateChannelView.js b/app/views/CreateChannelView.js index 8090ef4fe..54f402c63 100644 --- a/app/views/CreateChannelView.js +++ b/app/views/CreateChannelView.js @@ -83,13 +83,15 @@ class CreateChannelView extends React.Component { id: PropTypes.string, token: PropTypes.string }), - theme: PropTypes.string + theme: PropTypes.string, + teamId: PropTypes.string }; constructor(props) { super(props); const { route } = this.props; const isTeam = route?.params?.isTeam || false; + this.teamId = route?.params?.teamId; this.state = { channelName: '', type: true, @@ -180,7 +182,7 @@ class CreateChannelView extends React.Component { // create channel or team create({ - name: channelName, users, type, readOnly, broadcast, encrypted, isTeam + name: channelName, users, type, readOnly, broadcast, encrypted, isTeam, teamId: this.teamId }); Review.pushPositiveEvent(); diff --git a/app/views/NewMessageView.js b/app/views/NewMessageView.js index ae0628f19..cca5d5842 100644 --- a/app/views/NewMessageView.js +++ b/app/views/NewMessageView.js @@ -60,7 +60,7 @@ class NewMessageView extends React.Component { id: PropTypes.string, token: PropTypes.string }), - createChannel: PropTypes.func, + create: PropTypes.func, maxUsers: PropTypes.number, theme: PropTypes.string, isMasterDetail: PropTypes.bool @@ -124,9 +124,9 @@ class NewMessageView extends React.Component { createGroupChat = () => { logEvent(events.NEW_MSG_CREATE_GROUP_CHAT); - const { createChannel, maxUsers, navigation } = this.props; + const { create, maxUsers, navigation } = this.props; navigation.navigate('SelectedUsersViewCreateChannel', { - nextAction: () => createChannel({ group: true }), + nextAction: () => create({ group: true }), buttonText: I18n.t('Create'), maxUsers }); diff --git a/app/views/TeamChannelsView.js b/app/views/TeamChannelsView.js index 15724ab5c..03398edc9 100644 --- a/app/views/TeamChannelsView.js +++ b/app/views/TeamChannelsView.js @@ -1,11 +1,10 @@ import React from 'react'; -import { Keyboard } from 'react-native'; +import { Keyboard, Alert } from 'react-native'; import PropTypes from 'prop-types'; import { Q } from '@nozbe/watermelondb'; import { withSafeAreaInsets } from 'react-native-safe-area-context'; import { connect } from 'react-redux'; import { FlatList } from 'react-native-gesture-handler'; -import { HeaderBackButton } from '@react-navigation/stack'; import StatusBar from '../containers/StatusBar'; import RoomHeader from '../containers/RoomHeader'; @@ -23,11 +22,14 @@ import RoomItem, { ROW_HEIGHT } from '../presentation/RoomItem'; import RocketChat from '../lib/rocketchat'; import { withDimensions } from '../dimensions'; import { isIOS } from '../utils/deviceInfo'; -import { themes } from '../constants/colors'; import debounce from '../utils/debounce'; import { showErrorAlert } from '../utils/info'; import { goRoom } from '../utils/goRoom'; import I18n from '../i18n'; +import { withActionSheet } from '../containers/ActionSheet'; +import { deleteRoom as deleteRoomAction } from '../actions/room'; +import { CustomIcon } from '../lib/Icons'; +import { themes } from '../constants/colors'; const API_FETCH_COUNT = 25; @@ -47,7 +49,11 @@ class TeamChannelsView extends React.Component { theme: PropTypes.string, useRealName: PropTypes.bool, width: PropTypes.number, - StoreLastMessage: PropTypes.bool + StoreLastMessage: PropTypes.bool, + addTeamChannelPermission: PropTypes.array, + removeTeamChannelPermission: PropTypes.array, + showActionSheet: PropTypes.func, + deleteRoom: PropTypes.func } constructor(props) { @@ -60,9 +66,11 @@ class TeamChannelsView extends React.Component { isSearching: false, searchText: '', search: [], - end: false + end: false, + showCreate: false }; this.loadTeam(); + this.setHeader(); } componentDidMount() { @@ -70,6 +78,9 @@ class TeamChannelsView extends React.Component { } loadTeam = async() => { + const { addTeamChannelPermission } = this.props; + const { loading, data } = this.state; + const db = database.active; try { const subCollection = db.get('subscriptions'); @@ -82,6 +93,15 @@ class TeamChannelsView extends React.Component { if (!this.team) { throw new Error(); } + + const permissions = await RocketChat.hasPermission([addTeamChannelPermission], this.team.rid); + if (permissions[0]) { + this.setState({ showCreate: true }, () => this.setHeader()); + } + + if (loading && data.length) { + this.setState({ loading: false }); + } } catch { const { navigation } = this.props; navigation.pop(); @@ -115,14 +135,11 @@ class TeamChannelsView extends React.Component { loadingMore: false, end: result.rooms.length < API_FETCH_COUNT }; - const rooms = result.rooms.map((room) => { - const record = this.teamChannels?.find(c => c.rid === room._id); - return record ?? room; - }); + if (isSearching) { - newState.search = [...search, ...rooms]; + newState.search = [...search, ...result.rooms]; } else { - newState.data = [...data, ...rooms]; + newState.data = [...data, ...result.rooms]; } this.setState(newState); @@ -135,18 +152,16 @@ class TeamChannelsView extends React.Component { } }, 300) - getHeader = () => { - const { isSearching } = this.state; - const { - navigation, isMasterDetail, insets, theme - } = this.props; + setHeader = () => { + const { isSearching, showCreate, data } = this.state; + const { navigation, isMasterDetail, insets } = this.props; const { team } = this; if (!team) { return; } - const headerTitlePosition = getHeaderTitlePosition({ insets, numIconsRight: 1 }); + const headerTitlePosition = getHeaderTitlePosition({ insets, numIconsRight: 2 }); if (isSearching) { return { @@ -188,27 +203,16 @@ class TeamChannelsView extends React.Component { if (isMasterDetail) { options.headerLeft = () => ; - } else { - options.headerLeft = () => ( - navigation.pop()} - tintColor={themes[theme].headerTintColor} - /> - ); } options.headerRight = () => ( + { showCreate + ? navigation.navigate('AddChannelTeamView', { teamId: this.teamId, teamChannels: data })} /> + : null} ); - return options; - } - - setHeader = () => { - const { navigation } = this.props; - const options = this.getHeader(); navigation.setOptions(options); } @@ -287,6 +291,115 @@ class TeamChannelsView extends React.Component { } }, 1000, true); + options = (item) => { + const { theme } = this.props; + const isAutoJoinChecked = item.teamDefault; + const autoJoinIcon = isAutoJoinChecked ? 'checkbox-checked' : 'checkbox-unchecked'; + const autoJoinIconColor = isAutoJoinChecked ? themes[theme].tintActive : themes[theme].auxiliaryTintColor; + return ([ + { + title: I18n.t('Auto-join'), + icon: item.t === 'p' ? 'channel-private' : 'channel-public', + onPress: () => this.toggleAutoJoin(item), + right: () => + }, + { + title: I18n.t('Remove_from_Team'), + icon: 'close', + danger: true, + onPress: () => this.remove(item) + }, + { + title: I18n.t('Delete'), + icon: 'delete', + danger: true, + onPress: () => this.delete(item) + } + ]); + } + + toggleAutoJoin = async(item) => { + try { + const { data } = this.state; + const result = await RocketChat.updateTeamRoom({ roomId: item._id, isDefault: !item.teamDefault }); + if (result.success) { + const newData = data.map((i) => { + if (i._id === item._id) { + i.teamDefault = !i.teamDefault; + } + return i; + }); + this.setState({ data: newData }); + } + } catch (e) { + log(e); + } + } + + remove = (item) => { + Alert.alert( + I18n.t('Confirmation'), + I18n.t('Delete_Team_Room_Warning'), + [ + { + text: I18n.t('Cancel'), + style: 'cancel' + }, + { + text: I18n.t('Yes_action_it', { action: I18n.t('remove') }), + style: 'destructive', + onPress: () => this.removeRoom(item) + } + ], + { cancelable: false } + ); + } + + removeRoom = async(item) => { + try { + const { data } = this.state; + const result = await RocketChat.removeTeamRoom({ roomId: item._id, teamId: this.team.teamId }); + if (result.success) { + const newData = data.filter(room => result.room._id !== room._id); + this.setState({ data: newData }); + } + } catch (e) { + log(e); + } + } + + delete = (item) => { + const { deleteRoom } = this.props; + + Alert.alert( + I18n.t('Are_you_sure_question_mark'), + I18n.t('Delete_Room_Warning'), + [ + { + text: I18n.t('Cancel'), + style: 'cancel' + }, + { + text: I18n.t('Yes_action_it', { action: I18n.t('delete') }), + style: 'destructive', + onPress: () => deleteRoom(item._id, item.t) + } + ], + { cancelable: false } + ); + } + + showChannelActions = async(item) => { + logEvent(events.ROOM_SHOW_BOX_ACTIONS); + const { showActionSheet, removeTeamChannelPermission } = this.props; + + const permissions = await RocketChat.hasPermission([removeTeamChannelPermission], this.team.rid); + if (!permissions[0]) { + return; + } + showActionSheet({ options: this.options(item) }); + } + renderItem = ({ item }) => { const { StoreLastMessage, @@ -302,10 +415,12 @@ class TeamChannelsView extends React.Component { showLastMessage={StoreLastMessage} onPress={this.onPressItem} width={width} + onLongPress={this.showChannelActions} useRealName={useRealName} getRoomTitle={this.getRoomTitle} getRoomAvatar={this.getRoomAvatar} swipeEnabled={false} + autoJoin={item.teamDefault} /> ); }; @@ -365,7 +480,13 @@ const mapStateToProps = state => ({ user: getUserSelector(state), useRealName: state.settings.UI_Use_Real_Name, isMasterDetail: state.app.isMasterDetail, - StoreLastMessage: state.settings.Store_Last_Message + StoreLastMessage: state.settings.Store_Last_Message, + addTeamChannelPermission: state.permissions['add-team-channel'], + removeTeamChannelPermission: state.permissions['remove-team-channel'] }); -export default connect(mapStateToProps)(withDimensions(withSafeAreaInsets(withTheme(TeamChannelsView)))); +const mapDispatchToProps = dispatch => ({ + deleteRoom: (rid, t) => dispatch(deleteRoomAction(rid, t)) +}); + +export default connect(mapStateToProps, mapDispatchToProps)(withDimensions(withSafeAreaInsets(withTheme(withActionSheet(TeamChannelsView))))); diff --git a/storybook/stories/RoomItem.js b/storybook/stories/RoomItem.js index d39e92082..025fbf7f5 100644 --- a/storybook/stories/RoomItem.js +++ b/storybook/stories/RoomItem.js @@ -3,7 +3,6 @@ import React from 'react'; import { ScrollView, Dimensions } from 'react-native'; import { storiesOf } from '@storybook/react-native'; import { Provider } from 'react-redux'; -// import moment from 'moment'; import { themes } from '../../app/constants/colors'; import RoomItemComponent from '../../app/presentation/RoomItem/RoomItem'; @@ -94,6 +93,15 @@ stories.add('Alerts', () => ( )); +stories.add('Tag', () => ( + <> + + + + + +)); + stories.add('Last Message', () => ( <>