#464 Make the left menu more dynamic
Merged 6 years ago by abompard. Opened 6 years ago by abompard.
abompard/fedora-hubs feature/leftmenu  into  develop

@@ -52,7 +52,7 @@ 

              </div>

              <div className={`col-md-${this.props.hub.config.right_width} text-center align-self-center`}>

                <HubMembership />

-               { this.props.currentUser.perms.config_hub &&

+               { this.props.hub.perms.config &&

                  <div>

                    <HubConfig />

                    <EditModeButton />

@@ -53,7 +53,7 @@ 

      if (!this.props.hub.name) {

        return null;

      }

-     if (this.props.hub.user_hub && this.props.currentUser.perms.config_hub) {

+     if (this.props.hub.user_hub && this.props.hub.perms.config) {

        return null;  // The user's own hub.

      }

      let commonProps = {disabled: false, title: ""}

@@ -6,36 +6,62 @@ 

  

  class LeftMenu extends React.Component {

  

-   propTypes: {

-     menu: PropTypes.array,

-   }

-   defaultProps: {

-     menu: [],

-   }

- 

    render() {

+     let userEntries = [];

+     if (this.props.user.logged_in) {

+       userEntries = [

+         (

+           <LeftMenuEntry

+             key={this.props.user.hub}

+             url={this.props.user.hub}

+             icon="home"

+             text="My Hub"

+             />

+         ), (

+           <LeftMenuEntry

+             key={this.props.user.stream}

+             url={this.props.user.stream}

+             icon="home"

+             text="My Stream"

+             />

+         ),

+         ...this.props.user.bookmarks.map((entry) => (

+           <LeftMenuEntry

+             key={entry.url}

+             icon="bookmark"

+             text={entry.name}

+             url={entry.url}

+             cssClass={entry.cssClass}

+             />

+           ))

+       ];

+     }

      return (

        <div className="LeftMenu">

          <ul

            className="nav nav-pills flex-lg-column mb-0 rounded"

            role="navigation"

            >

-           {this.props.menu.map((entry) => (

-             <LeftMenuEntry

-               key={entry.url}

-               {...entry}

-               />

-             ))

-           }

+           {userEntries}

+           <LeftMenuEntry

+             url={this.props.allGroupsUrl}

+             icon="users"

+             text="All groups"

+             />

          </ul>

        </div>

      );

    }

  }

+ LeftMenu.propTypes = {

+   user: PropTypes.object,

+   allGroupsUrl: PropTypes.string,

+ }

  

  const mapStateToProps = (state) => {

    return {

-     menu: state.ui.leftMenu,

+     user: state.currentUser,

+     allGroupsUrl: state.urls.allGroups,

    }

  };

  
@@ -44,16 +70,6 @@ 

  

  class LeftMenuEntry extends React.Component {

  

-   propTypes: {

-     url: PropTypes.string.isRequired,

-     icon: PropTypes.string.isRequired,

-     text: PropTypes.string.isRequired,

-     cssClass: PropTypes.string,

-   }

-   defaultProps: {

-     cssClass: null,

-   }

- 

    render() {

      const isActive = window.location.pathname.indexOf(this.props.url) !== -1;

      let cssClass = "nav-link";
@@ -74,3 +90,13 @@ 

      );

    }

  }

+ 

+ LeftMenuEntry.propTypes = {

+   url: PropTypes.string.isRequired,

+   icon: PropTypes.string.isRequired,

+   text: PropTypes.string.isRequired,

+   cssClass: PropTypes.string,

+ }

+ LeftMenuEntry.defaultProps = {

+   cssClass: null,

+ }

@@ -1,5 +1,6 @@ 

  import { apiCall } from '../utils';

  import { addFlashMessage } from './flashMessages';

+ import { fetchCurrentUser } from './user';

  

  

  /* GET */
@@ -113,6 +114,7 @@ 

      dispatch(addFlashMessage(result.message, "info"));

    }

    dispatch(receiveHubAssoc(role, result.users, result.perms));

+   dispatch(fetchCurrentUser());

  }

  

  function associateError(dispatch, role, error) {

@@ -0,0 +1,20 @@ 

+ import { apiCall } from '../utils';

+ 

+ export const USER_FETCH_SUCCESS = 'USER_FETCH_SUCCESS';

+ 

+ function receiveUser(user) {

+   return {

+     type: USER_FETCH_SUCCESS,

+     user: user,

+     receivedAt: Date.now()

+   }

+ }

+ 

+ export function fetchCurrentUser() {

+   return (dispatch, getState) => {

+     let url = getState().urls.user;

+     return apiCall(url).then(

+       user => dispatch(receiveUser(user)),

+     );

+   }

+ }

@@ -59,6 +59,7 @@ 

        return {

          ...state,

          users: action.users,

+         perms: action.perms,

          isLoading: false,

        };

      default:

@@ -14,6 +14,11 @@ 

  import { userReducer } from "./user";

  

  

+ function noop(state=null, action) {

+   return state;

+ }

+ 

+ 

  const entities = combineReducers({

    hub: hubReducer,

    widgets: widgetsReducer,
@@ -26,15 +31,9 @@ 

    flashMessages,

    widgetsEditMode,

    widgetConfigDialogOpen,

-   leftMenu: noop,

  });

  

  

- function noop(state=null, action) {

-   return state;

- }

- 

- 

  const rootReducer = combineReducers({

    flashMessages,

    sse: sseReducer,

@@ -1,15 +1,15 @@ 

  import {

    HUB_ASSOC_SUCCESS,

    } from '../actions/hub';

+ import {

+   USER_FETCH_SUCCESS,

+   } from '../actions/user';

  

  

  export function userReducer(state=null, action) {

    switch (action.type) {

-     case HUB_ASSOC_SUCCESS:

-       return {

-         ...state,

-         perms: action.perms,

-       };

+     case USER_FETCH_SUCCESS:

+       return action.user;

      default:

        return state

    }

@@ -13,7 +13,7 @@ 

      let content;

      if (!domain || !channel) {

        content = "No IRC channel configured.";

-       if (this.props.currentUser.perms.config_hub) {

+       if (this.props.currentUser.perms.config) {

          content += " Go to \"Hubs settings\" to configure it.";

        }

        content = (

@@ -30,6 +30,7 @@ 

              "user_hub": True,

              "mtime": "Sun, 01 Jan 2017 00:00:00 GMT",

              "subscribed_to": [],

+             'perms': {'config': True},

              "config": {

                  "avatar": avatar_url,

                  'chat_channel': None,

@@ -13,7 +13,6 @@ 

          state = response.context["initial_state"]

          self.assertEqual(state["urls"]["hubConfig"], "/api/hubs/ralph/config")

          self.assertFalse(state["currentUser"]["logged_in"])

-         self.assertFalse(state["currentUser"]["perms"]["config_hub"])

  

      def test_hub_logged_in(self):

          user = FakeAuthorization('ralph')
@@ -23,7 +22,6 @@ 

              response.get_data(as_text=True))

          state = response.context["initial_state"]

          self.assertTrue(state["currentUser"]["logged_in"])

-         self.assertTrue(state["currentUser"]["perms"]["config_hub"])

  

      def test_hub_preview(self):

          hub = Hub.by_name('ralph')

file modified
+17 -1
@@ -49,6 +49,22 @@ 

          flask.abort(404)

  

  

+ def get_user_details():

+     current_user = flask.g.auth.copy()

+     if flask.g.auth.logged_in:

+         user = flask.g.user

+         current_user["hub"] = flask.url_for("hub", name=user.username)

+         current_user["stream"] = flask.url_for("stream")

+         current_user["bookmarks"] = []

+         for hub in user.bookmarks:

+             current_user["bookmarks"].append({

+                 "name": hub.name,

+                 "url": flask.url_for("hub", name=hub.name),

+                 "cssClass": "idle-{}".format(hub.activity_class),

+             })

+     return current_user

+ 

+ 

  def get_menu_entries():

      """Get the entries for the left menu."""

      menu = []
@@ -243,7 +259,7 @@ 

          hub (hubs.models.Hub): A Hub instance.

      """

      return {

-         "config_hub": hub.allows(flask.g.user, "config"),

+         "config": hub.allows(flask.g.user, "config"),

      }

  

  

@@ -6,3 +6,4 @@ 

  from .hub_association import *

  from .hub_config import *

  from .hub_widget import *

+ from .user import *

file modified
+4 -2
@@ -3,12 +3,14 @@ 

  import flask

  

  from hubs.app import app

- from hubs.utils.views import get_hub, require_hub_access

+ from hubs.utils.views import get_hub, get_user_permissions, require_hub_access

  

  

  @app.route('/api/hubs/<name>/', methods=['GET'])

  @require_hub_access("view", json=True)

  def api_hub(name):

      hub = get_hub(name)

-     result = {"status": "OK", "data": hub.get_props()}

+     data = hub.get_props()

+     data["perms"] = get_user_permissions(hub)

+     result = {"status": "OK", "data": data}

      return flask.jsonify(result)

file modified
+5 -2
@@ -7,7 +7,8 @@ 

  import hubs.models

  from hubs.app import app

  from hubs.utils.views import (

-     get_hub, check_hub_access, RequestValidator, require_hub_access,

+     get_hub, get_user_permissions, check_hub_access, RequestValidator,

+     require_hub_access,

      )

  

  log = logging.getLogger(__name__)
@@ -43,7 +44,9 @@ 

          except Exception as err:

              result = {"status": "ERROR", "message": str(err)}

          return flask.jsonify(result)

-     result = {"status": "OK", "data": hub.get_props()}

+     data = hub.get_props()

+     data["perms"] = get_user_permissions(hub)

+     result = {"status": "OK", "data": data}

      return flask.jsonify(result)

  

  

@@ -0,0 +1,13 @@ 

+ from __future__ import absolute_import, unicode_literals

+ 

+ import flask

+ 

+ from hubs.app import app

+ from hubs.utils.views import get_user_details, login_required

+ 

+ 

+ @app.route('/api/user/', methods=['GET'])

+ @login_required

+ def api_user():

+     result = {"status": "OK", "data": get_user_details()}

+     return flask.jsonify(result)

file modified
+4 -6
@@ -5,8 +5,7 @@ 

  

  from hubs.app import app

  from hubs.utils.views import (

-     get_hub, get_sse_url, get_menu_entries, require_hub_access,

-     get_user_permissions

+     get_hub, get_sse_url, require_hub_access, get_user_details,

      )

  

  
@@ -27,10 +26,10 @@ 

          "hubConfig": flask.url_for("api_hub_config", name=hub.name),

          "hubConfigSuggestUsers": flask.url_for(

              "api_hub_config_suggest_users", name=hub.name),

+         "user": flask.url_for("api_user"),

+         "allGroups": flask.url_for("groups"),

      }

-     current_user = flask.g.auth.copy()

-     current_user["perms"] = get_user_permissions(hub)

-     menu = get_menu_entries()

+     current_user = get_user_details()

      flash_messages = [

          {"msg": msg[1], "type": msg[0]} for msg in

          flask.get_flashed_messages(with_categories=True)
@@ -42,7 +41,6 @@ 

              ui=dict(

                  page="Hub",

                  flashMessages=flash_messages,

-                 leftMenu=menu,

              ),

              globalConfig=global_config,

              urls=urls,

file modified
+3 -4
@@ -6,20 +6,20 @@ 

  

  from hubs.app import app

  from hubs.utils.views import (

-     login_required, get_sse_url, get_menu_entries)

+     login_required, get_sse_url, get_user_details)

  

  

  @app.route('/stream')

  @app.route('/stream/')

  @login_required

  def stream():

-     current_user = flask.g.auth.copy()

+     current_user = get_user_details()

      urls = {

          "sse": get_sse_url("user/{}".format(current_user["nickname"])),

          "notifications": flask.url_for("stream_existing"),

          "saved": flask.url_for("saved_notifs"),

+         "allGroups": flask.url_for("groups"),

      }

-     menu = get_menu_entries()

      flash_messages = [

          {"msg": msg[1], "type": msg[0]} for msg in

          flask.get_flashed_messages(with_categories=True)
@@ -31,7 +31,6 @@ 

              ui=dict(

                  page="Streams",

                  flashMessages=flash_messages,

-                 leftMenu=menu,

              ),

              urls=urls,

              currentUser=current_user,