1- import { DAO } from "@daostack/client" ;
1+ import { DAO , DAOFieldsFragment } from "@daostack/client" ;
22import { getArc } from "arc" ;
33import Loading from "components/Shared/Loading" ;
44import withSubscription , { ISubscriptionProps } from "components/Shared/withSubscription" ;
55import gql from "graphql-tag" ;
66import Analytics from "lib/analytics" ;
7+ import { createDaoStateFromQuery } from "lib/daoHelpers" ;
78import { Page } from "pages" ;
89import * as React from "react" ;
910import { BreadcrumbsItem } from "react-breadcrumbs-dynamic" ;
@@ -12,24 +13,23 @@ import { connect } from "react-redux";
1213import * as Sticky from "react-stickynode" ;
1314import { Link } from "react-router-dom" ;
1415import { IRootState } from "reducers" ;
15- import { IProfileState } from "reducers/profilesReducer" ;
1616import { combineLatest , of } from "rxjs" ;
1717import { first } from "rxjs/operators" ;
1818
1919import DaoCard from "./DaoCard" ;
2020import * as css from "./Daos.scss" ;
2121
22- type SubscriptionData = [ DAO [ ] , string [ ] ] ;
22+ type SubscriptionData = [ DAO [ ] , DAO [ ] , DAO [ ] ] ;
2323
2424interface IStateProps {
2525 currentAccountAddress : string ;
26- currentAccountProfile : IProfileState ;
26+ followingDAOs : string [ ] ;
2727}
2828
2929const mapStateToProps = ( state : IRootState ) : IStateProps => {
3030 return {
3131 currentAccountAddress : state . web3 . currentAccountAddress ,
32- currentAccountProfile : state . profiles [ state . web3 . currentAccountAddress ] ,
32+ followingDAOs : state . profiles [ state . web3 . currentAccountAddress ] ? state . profiles [ state . web3 . currentAccountAddress ] . follows . daos : [ ] ,
3333 } ;
3434} ;
3535
@@ -81,39 +81,46 @@ class DaosPage extends React.Component<IProps, IState> {
8181 }
8282
8383 public render ( ) : RenderOutput {
84- const { currentAccountProfile , data, fetchMore } = this . props ;
84+ const { data, fetchMore } = this . props ;
8585 const search = this . state . search . length > 2 ? this . state . search . toLowerCase ( ) : "" ;
8686
87- let allDAOs = data [ 0 ] ;
87+ // Always show DAOs that the current user is a member of or follows first
88+ const yourDAOs = data [ 1 ] . concat ( data [ 2 ] ) . filter ( d => d . staticState . name . toLowerCase ( ) . includes ( search ) ) . sort ( ( a , b ) => a . staticState . name . localeCompare ( b . staticState . name ) ) ;
89+ const yourDAOAddresses = yourDAOs . map ( dao => dao . id ) ;
8890
91+ // Then all the rest of the DAOs
92+ let otherDAOs = data [ 0 ] ;
8993 // Add any DAOs found from searching the server to the list
9094 if ( this . state . searchDaos . length > 0 ) {
9195 // make sure we don't add duplicate DAOs to the list
9296 const extraFoundDaos = this . state . searchDaos . filter ( ( dao ) => {
93- return ! allDAOs . find ( ( d ) => d . id === dao . id ) ;
97+ return ! otherDAOs . find ( ( d ) => d . id === dao . id ) ;
9498 } ) ;
95- allDAOs = allDAOs . concat ( extraFoundDaos ) ;
99+ otherDAOs = otherDAOs . concat ( extraFoundDaos ) ;
96100 }
97101
98- // Always show Genesis Alpha first
99- let finalDAOList = allDAOs . filter ( ( d : DAO ) => d . staticState . name === "Genesis Alpha" && d . staticState . name . toLowerCase ( ) . includes ( search ) ) ;
100-
101102 if ( process . env . NODE_ENV === "staging" ) {
102103 // on staging we show all daos (registered or not)
103- finalDAOList = finalDAOList . concat ( allDAOs . filter ( ( d : DAO ) => d . staticState . name !== "Genesis Alpha" && d . staticState . name . toLowerCase ( ) . includes ( search ) ) ) ;
104+ otherDAOs = otherDAOs . filter ( ( d : DAO ) => ! yourDAOAddresses . includes ( d . id ) && d . staticState . name . toLowerCase ( ) . includes ( search ) ) ;
104105 } else {
105- // Otherwise show registered DAOs or DAOs that the person follows or is a member of
106- const memberOfDAOs = data [ 1 ] ;
107- finalDAOList = finalDAOList . concat ( allDAOs . filter ( ( d : DAO ) => {
108- return d . staticState . name !== "Genesis Alpha" &&
106+ // Otherwise show registered DAOs
107+ otherDAOs = otherDAOs . filter ( ( d : DAO ) => {
108+ return ! yourDAOAddresses . includes ( d . id ) &&
109109 d . staticState . name . toLowerCase ( ) . includes ( search ) &&
110- ( d . staticState . register === "registered" ||
111- ( currentAccountProfile && currentAccountProfile . follows . daos . includes ( d . staticState . address ) ) ||
112- memberOfDAOs . includes ( d . staticState . address ) ) ;
113- } ) ) ;
110+ d . staticState . register === "registered" ;
111+ } ) ;
114112 }
115113
116- const daoNodes = finalDAOList . map ( ( dao : DAO ) => {
114+ const yourDaoNodes = yourDAOs . map ( ( dao : DAO ) => {
115+ return (
116+ < DaoCard
117+ key = { dao . id }
118+ dao = { dao }
119+ />
120+ ) ;
121+ } ) ;
122+
123+ const otherDaoNodes = otherDAOs . map ( ( dao : DAO ) => {
117124 return (
118125 < DaoCard
119126 key = { dao . id }
@@ -126,24 +133,45 @@ class DaosPage extends React.Component<IProps, IState> {
126133 < div className = { css . wrapper } >
127134 < BreadcrumbsItem to = "/daos/" > All DAOs</ BreadcrumbsItem >
128135
136+ < Link to = { "/daos/create" } className = { css . createDaoButton } >
137+ Create A DAO
138+ </ Link >
139+
129140 < Sticky enabled top = { 50 } innerZ = { 10000 } >
130141 < div className = { css . headerWrapper } >
131142 < div className = { css . headerTitle + " clearfix" } >
132- < h2 data-test-id = "header-all-daos" > All DAOs</ h2 >
143+ < h2 data-test-id = "header-all-daos" > Your DAOs</ h2 >
144+ </ div >
145+ { yourDAOs . length ?
146+ < div className = { css . searchBox } >
147+ < span > Search:</ span >
148+ < input type = "text" name = "search" placeholder = "DAO Name" onChange = { this . onSearchChange } value = { this . state . search } />
149+ </ div >
150+ : "" }
151+ </ div >
152+ </ Sticky >
153+ { yourDAOs . length ?
154+ < div className = { css . daoList } >
155+ { yourDaoNodes }
156+ </ div >
157+ : < h2 > Look for DAOs to join or follow below</ h2 >
158+ }
159+
160+ < Sticky enabled top = { 50 } innerZ = { 10000 } >
161+ < div className = { css . headerWrapper } >
162+ < div className = { css . headerTitle + " clearfix" } >
163+ < h2 data-test-id = "header-all-daos" > Other DAOs</ h2 >
133164 </ div >
134165 < div className = { css . searchBox } >
135166 < span > Search:</ span >
136167 < input type = "text" name = "search" placeholder = "DAO Name" onChange = { this . onSearchChange } value = { this . state . search } />
137168 </ div >
138- < Link to = { "/daos/create" } className = { css . createDaoButton } >
139- Create A DAO
140- </ Link >
141169 </ div >
142170 </ Sticky >
143171 < div className = { css . daoList } >
144- { daoNodes ?
172+ { otherDaoNodes ?
145173 < InfiniteScroll
146- dataLength = { finalDAOList . length } // This is important field to render the next data
174+ dataLength = { otherDaoNodes . length } // This is important field to render the next data
147175 next = { fetchMore }
148176 hasMore
149177 loader = ""
@@ -153,69 +181,65 @@ class DaosPage extends React.Component<IProps, IState> {
153181 </ p >
154182 }
155183 >
156- { daoNodes }
184+ { otherDaoNodes }
157185 </ InfiniteScroll > : "None" }
158186 </ div >
159187 </ div >
160188 ) ;
161189 }
162190}
163191
192+ const createSubscriptionObservable = ( props : IStateProps , data : SubscriptionData = null ) => {
193+ const arc = getArc ( ) ;
194+ const { currentAccountAddress, followingDAOs } = props ;
195+
196+ // TODO: right now we don't handle a user following or being a member of more than 100 DAOs
197+ // it was too hard to figure out the UI with infinite scrolling in this case we would need a different UI
198+
199+ // Get list of DAO addresses the current user is a member of,
200+ // ignoring ones that they are following so we dont show those twice
201+ const memberDAOsquery = gql `
202+ query ReputationHolderSearch {
203+ reputationHolders(where: {
204+ address: "${ currentAccountAddress } "
205+ ${ followingDAOs . length ? "dao_not_in: [" + followingDAOs . map ( dao => "\"" + dao + "\"" ) . join ( "," ) + "]" : "" }
206+ },
207+ ) {
208+ dao {
209+ ...DAOFields
210+ }
211+ }
212+ }
213+ ${ DAOFieldsFragment }
214+ ` ;
215+ const memberOfDAOs = currentAccountAddress ? arc . getObservableList ( memberDAOsquery , ( r : any ) => createDaoStateFromQuery ( r . dao ) . dao , { subscribe : true } ) : of ( [ ] ) ;
216+ // eslint-disable-next-line @typescript-eslint/camelcase
217+ const followDAOs = followingDAOs . length ? arc . daos ( { where : { id_in : followingDAOs } , orderBy : "name" , orderDirection : "asc" } , { fetchAllData : true , subscribe : true } ) : of ( [ ] ) ;
218+
219+ return combineLatest (
220+ arc . daos ( { orderBy : "name" , orderDirection : "asc" , first : PAGE_SIZE , skip : data ? data [ 0 ] . length : 0 } , { fetchAllData : true , subscribe : true } ) ,
221+ followDAOs ,
222+ memberOfDAOs
223+ ) ;
224+ } ;
225+
164226const SubscribedDaosPage = withSubscription ( {
165227 wrappedComponent : DaosPage ,
166228 loadingComponent : < div className = { css . wrapper } > < Loading /> </ div > ,
167229 errorComponent : ( props ) => < div > { props . error . message } </ div > ,
168230
169231 // Don't ever update the subscription
170- checkForUpdate : [ "currentAccountAddress" ] ,
232+ checkForUpdate : [ "currentAccountAddress" , "followingDAOs" ] ,
171233
172234 // used for hacky pagination tracking
173235 pageSize : PAGE_SIZE ,
174236
175- createObservable : ( props : IStateProps ) => {
176- const arc = getArc ( ) ;
177-
178- // Get list of DAO addresses the current user is a member of
179- const memberDAOsquery = gql `
180- query ReputationHolderSearch {
181- reputationHolders(where: { address: "${ props . currentAccountAddress } " }, first: ${ PAGE_SIZE } , skip: 0) {
182- dao {
183- id
184- }
185- }
186- }
187- ` ;
188- const memberOfDAOs = props . currentAccountAddress ? arc . getObservableList ( memberDAOsquery , ( r : any ) => r . dao . id , { subscribe : true } ) : of ( [ ] ) ;
189-
190- return combineLatest (
191- arc . daos ( { orderBy : "name" , orderDirection : "asc" , first : PAGE_SIZE , skip : 0 } , { fetchAllData : true , subscribe : true } ) ,
192- memberOfDAOs
193- ) ;
194- } ,
237+ createObservable : createSubscriptionObservable ,
195238
196- getFetchMoreObservable : ( props : IStateProps , data : SubscriptionData ) => {
197- const arc = getArc ( ) ;
198-
199- // Get list of DAO addresses the current user is a member of
200- const memberDAOsquery = gql `
201- query ReputationHolderSearch {
202- reputationHolders(where: { address: "${ props . currentAccountAddress } " }, first: ${ PAGE_SIZE } , skip: ${ data [ 1 ] . length } ) {
203- dao {
204- id
205- }
206- }
207- }
208- ` ;
209- const memberOfDAOs = props . currentAccountAddress ? arc . getObservableList ( memberDAOsquery , ( r : any ) => r . dao . id , { subscribe : true } ) : of ( [ ] ) ;
210-
211- return combineLatest (
212- arc . daos ( { orderBy : "name" , orderDirection : "asc" , first : PAGE_SIZE , skip : data [ 0 ] . length } , { fetchAllData : true , subscribe : true } ) ,
213- memberOfDAOs
214- ) ;
215- } ,
239+ getFetchMoreObservable : createSubscriptionObservable ,
216240
217241 fetchMoreCombine : ( prevData : SubscriptionData , newData : SubscriptionData ) => {
218- return [ prevData [ 0 ] . concat ( newData [ 0 ] ) , prevData [ 1 ] . concat ( newData [ 1 ] ) ] as SubscriptionData ;
242+ return [ prevData [ 0 ] . concat ( newData [ 0 ] ) , prevData [ 1 ] , prevData [ 2 ] ] as SubscriptionData ;
219243 } ,
220244} ) ;
221245
0 commit comments