Gitter migration: Setup redirects (rollout pt. 3)
[gitter.git] / server / handlers / renderers / explore-renderer.js
blobca71fd1fca1d2da4966c39da1e6a51a8b3ec20cc
1 'use strict';
3 const _ = require('lodash');
4 const urlJoin = require('url-join');
5 const asyncHandler = require('express-async-handler');
7 const clientEnv = require('gitter-client-env');
8 const contextGenerator = require('../../web/context-generator');
9 const mixinHbsDataForVueLeftMenu = require('./vue/mixin-vue-left-menu-data');
10 const exploreService = require('../../services/explore-service');
11 const suggestionsService = require('../../services/suggestions-service');
12 const exploreTagUtils = require('../../utils/explore-tag-utils');
13 const generateExploreSnapshot = require('../snapshots/explore-snapshot');
14 const generateUserThemeSnapshot = require('../snapshots/user-theme-snapshot');
15 const fonts = require('../../web/fonts');
16 const isMobile = require('../../web/is-phone');
18 function getExploreBaseUrl(req) {
19   return urlJoin(req.baseUrl, req.path).replace(/\/(|tags.*)$/, '');
22 function processTagInput(req) {
23   let input = req.params.tags;
24   if (!input) {
25     const urlTagStringMatches = req.path.match(/\/tags\/(.*)/);
26     input = urlTagStringMatches && urlTagStringMatches[1];
27   }
29   input = input || exploreTagUtils.tagConstants.SUGGESTED_TAG_LABEL.toLowerCase();
30   const selectedTagsInput = input
31     .split(',')
32     .filter(function(inputItem) {
33       return inputItem.trim().length > 0;
34     })
35     .map(function(tag) {
36       return tag.toLowerCase();
37     });
39   return selectedTagsInput;
42 const FAUX_TAG_MAP = {};
43 FAUX_TAG_MAP[exploreTagUtils.tagConstants.SUGGESTED_TAG_LABEL] = [
44   exploreTagUtils.tagConstants.SUGGESTED_BACKEND_TAG
46 _.extend(FAUX_TAG_MAP, {
47   Frontend: [],
48   Mobile: ['curated:ios', 'curated:android', 'objective-c'],
49   iOS: [],
50   Android: [],
51   'Data Science': [],
52   Devops: ['devops'],
53   'Game Dev': ['game'],
54   Frameworks: ['frameworks'],
55   JavaScript: ['javascript'],
56   Scala: ['scala'],
57   Ruby: ['ruby'],
58   CSS: ['css'],
59   'Material Design': [],
60   React: ['react'],
61   Java: ['java'],
62   PHP: ['php'],
63   Swift: ['swift'],
64   Go: ['go'],
65   Node: ['node', 'nodejs'],
66   Meteor: ['meteor'],
67   Django: ['django'],
68   '.NET': ['dotnet'],
69   Angular: ['angular'],
70   Rails: ['rails'],
71   Haskell: ['haskell']
72 });
74 async function renderExplorePage(req, res) {
75   const troupeContext = await contextGenerator.generateBasicContext(req);
76   const user = troupeContext.user;
77   const isLoggedIn = !!user;
79   // Copy so we can modify later on
80   const fauxTagMap = _.extend({}, FAUX_TAG_MAP);
82   const selectedTagsInput = processTagInput(req)
83     // We only take one selected tag
84     .slice(0, 1);
86   // We only generate the tag map here to grab the list of selected tags so
87   // we can populate our rooms from the explore service
88   const tagMap = exploreTagUtils.generateTagMap(fauxTagMap);
89   const selectedTagMap = exploreTagUtils.getSelectedEntriesInTagMap(tagMap, selectedTagsInput);
91   let hasSuggestedTag = false;
93   // Mush into an array of selected tags
94   const selectedBackendTags = Object.keys(selectedTagMap).reduce(function(prev, key) {
95     // Check for the selected tag for easy reference later
96     selectedTagMap[key].tags.forEach(function(tag) {
97       if (tag === exploreTagUtils.tagConstants.SUGGESTED_BACKEND_TAG) {
98         hasSuggestedTag = true;
99       }
100     });
102     return prev.concat(selectedTagMap[key].tags);
103   }, []);
105   let userSuggestions;
106   if (hasSuggestedTag && isLoggedIn) {
107     const suggestedRooms = await suggestionsService.findSuggestionsForUserId(user.id);
109     if (suggestedRooms && suggestedRooms.length) {
110       userSuggestions = suggestedRooms.map(function(room) {
111         room.tags = room.tags || [];
112         room.tags.push(exploreTagUtils.tagConstants.SUGGESTED_BACKEND_TAG);
113         return room;
114       });
115     }
116   }
118   let rooms;
119   if (userSuggestions && userSuggestions.length) {
120     rooms = userSuggestions;
121   } else {
122     rooms = await exploreService.fetchByTags(selectedBackendTags);
123   }
125   const snapshots = await generateExploreSnapshot({
126     isLoggedIn: isLoggedIn,
127     fauxTagMap: fauxTagMap,
128     selectedTags: selectedTagsInput,
129     rooms: rooms
130   });
132   //Not 100% sure this is the best thing to do here
133   //but I dont really want to refactor this whole thing
134   const userThemeSnapshot = await generateUserThemeSnapshot(req);
135   // Anyone know why we're putting this on the
136   // context? Probably not.
137   troupeContext.snapshots = snapshots;
139   return res.render(
140     'explore',
141     await mixinHbsDataForVueLeftMenu(
142       req,
143       _.extend({}, snapshots, {
144         bootScriptName: 'explore',
145         cssFileName: 'styles/explore.css',
146         hasDarkTheme: userThemeSnapshot.theme === 'gitter-dark',
147         isMobile: isMobile(req),
148         elementUrl: clientEnv.elementUrl,
149         exploreBaseUrl: getExploreBaseUrl(req),
150         troupeContext: troupeContext,
151         isLoggedIn: isLoggedIn,
152         createRoomUrl: urlJoin(clientEnv.basePath, '#createroom'),
153         fonts: fonts.getFonts(),
154         hasCachedFonts: fonts.hasCachedFonts(req.cookies)
155       })
156     )
157   );
160 module.exports = {
161   renderExplorePage: asyncHandler(renderExplorePage)