                 value = @props.search
                 ref="search"
                 placeholder="Search for projects..."
-                onChange={@search}
+                onChange={=>flux.getActions('projects').setTo(search: @refs.search.getValue())}
                 buttonAfter={@delete_search_button()} />
         </Col>
 
-ProjectsHashtagGroup = rclass
-    propTypes:
-        hashtags : rtypes.object.isRequired
-
-    getDefaultProps: ->
-        hashtags : {}
-
-    toggleButton: (tag) ->
-        hashtags = @props.hashtags
-        hashtags[tag] = not hashtags[tag]
-        localStorage.projects_hashtags = JSON.stringify(hashtags)
-        flux.getActions('projects').setTo
-            hashtags : hashtags
-
-    render_hashtag: (tag, selected) ->
-        <Button key={tag} onClick={=>@toggleButton(tag)} bsSize="small" bsStyle={if selected then "warning" else "info"}>{"#" + tag}</Button>
-
-    render: ->
-        <Col sm=8>
-            <ButtonGroup>
-                {@render_hashtag(tag, selected) for tag, selected of @props.hashtags}
-            </ButtonGroup>
-        </Col>
+HashtagGroup = rclass
+    propTypes:
+        hashtags : rtypes.array.isRequired
+        selected_hashtags : rtypes.object.isRequired
+        toggle_hashtag : rtypes.func.isRequired
+
+    render_hashtag: (tag) ->
+        color = "info"
+        if @props.selected_hashtags and @props.selected_hashtags[tag]
+            color = "warning"
+        <Button key={tag} onClick={=>@props.toggle_hashtag(tag)} bsSize="small" bsStyle={color}>{tag}</Button>
+
+    render: ->
+        <ButtonGroup>
+            {@render_hashtag(tag) for tag in @props.hashtags}
+        </ButtonGroup>
 
 ProjectsListingDescription = rclass
     propTypes:
         deleted : rtypes.bool.isRequired
         hidden : rtypes.bool.isRequired
-        hashtags : rtypes.object.isRequired
+        selected_hashtags : rtypes.object.isRequired
         search : rtypes.string.isRequired
 
     getDefaultProps: ->
         deleted : false
         hidden : false
-        hashtags : {}
+        selected_hashtags : {}
         search : ""
 
     description: ->
-        query = find_text(@props.search, @props.hashtags)
+        query = @props.search.toLowerCase()
+        #TODO: cached function
+        for tag of @props.selected_hashtags
+            query += " " + tag
         desc = "Showing "
         if @props.deleted
             desc += "deleted "


    render: ->
        <Button onClick={@show_all_projects} bsStyle="info" bsSize="large">Show all matching projects...</Button>
ProjectList = rclass
    propTypes:
        hidden : rtypes.bool
        deleted : rtypes.bool
        project_list : rtypes.array
        hidden_project_list : rtypes.array
        search : rtypes.string.isRequired

    getDefaultProps: ->
        hidden : false
        deleted : false
        project_list : []
        hidden_project_list : []
        search : ""

    search_term: cached_function((project) ->
        search = (project.title + ' ' + project.description).toLowerCase()
        for k in misc.split(search)
            if k[0] == '#'
                tag = k.slice(1).toLowerCase()
                search += " [#{k}] "
        for group in misc.PROJECT_GROUPS
            if project[group]?
                for user in project[group]
                    if user.account_id != salvus_client.account_id
                        search += (' ' + user.first_name + ' ' + user.last_name + ' ').toLowerCase()
        return search
        )

    matches: (project, words) ->
        search = @search_term(project)
        if words != []
            for word in words
                if word[0] == '#'
                    word = '[' + word + ']'
                if search.indexOf(word) == -1
                    return false
        return true

    render_row: (project, key) ->
        <ProjectRow project={project} key={key}/>

    render_list: ->
        MAX_DEFAULT_PROJECTS = 50
        query = find_text(@props.search, @props.hashtags)
        words = misc.split(query)
        listing = []
        i = 0
        v = (if @props.hidden then @props.hidden_project_list else @props.project_list)
        for project in v
            if not @props.show_all and i >= MAX_DEFAULT_PROJECTS
                listing.push <ShowAllMatchingProjectsButton show_all={@props.show_all}/>
                break
            if @matches(project, words)
                i += 1
                if (@props.deleted and project.deleted) or ((not @props.deleted) and (not project.deleted))
                    listing.push @render_row(project, i)
        listing

    render: ->
        <Col sm=12>

        return true

    visible_projects: ->
        words = misc.split(@props.search).concat((k for k of @props.selected_hashtags[@filter()]))
        return (project for project in @props.project_list when @project_is_in_filter(project, @props.hidden, @props.deleted) and @matches(project, words))

    toggle_hashtag: (tag) ->
        selected_hashtags = JSON.parse(JSON.stringify(@props.selected_hashtags))
        filter = @filter()
        if not selected_hashtags[filter]
            selected_hashtags[filter] = {}
        if selected_hashtags[filter][tag]
            delete selected_hashtags[filter][tag]
        else
            selected_hashtags[filter][tag] = true

    filter: ->
        "#{@props.hidden}-#{@props.deleted}"

    render: ->
        if not @props.project_list?
