Commit 9540dd2c authored by Eric Eastwood's avatar Eric Eastwood

Merge branch 'release/19.15.0'

parents 9177b029 da057f0c
# 19.15.0 - 2018-8-8
- Add feature toggle for embeds and disable by default, https://gitlab.com/gitlab-org/gitter/webapp/merge_requests/1223
Developer facing:
- Gitter Android app is now open-source, https://gitlab.com/gitlab-org/gitter/gitter-android-app
- Move Android embedded chat build to cross-platform Gulp scripts, https://gitlab.com/gitlab-org/gitter/webapp/merge_requests/1218
- Separate Android and iOS builds (restore chat input for Android), https://gitlab.com/gitlab-org/gitter/webapp/merge_requests/1222
# 19.14.0 - 2018-8-1
- Remove missing 404 rooms from the homepage, https://gitlab.com/gitlab-org/gitter/webapp/merge_requests/1215
......
EMBEDDED_NODE_ENV ?= prod
EMBEDDED_WWW_DIRECTORY ?= ~/code/gitter/ios/Troupe/www/build
export PATH := ./node_modules/.bin:$(PATH)
.PHONY: build clean test npm sprites npm-quick npm-full performance-tests test-no-coverage continuous-integration validate
......@@ -72,26 +70,6 @@ upgrade-data:
maintain-data:
MODIFY=true ./scripts/datamaintenance/execute.sh || true
clean-embedded-chat:
rm -rf output/embedded output/embedded.tgz
embedded-chat: clean
mkdir -p output/embedded/www/mobile
NODE_ENV=$(EMBEDDED_NODE_ENV) ./build-scripts/render-embedded-chat.js -o output/embedded/www/mobile/embedded-chat.html
gulp --gulpfile gulpfile-embedded.js
ls output/assets/js/*.js >> output/embedded-resources.txt
ls output/assets/styles/*.css >> output/embedded-resources.txt
ls output/assets/images/emoji/* >> output/embedded-resources.txt
./build-scripts/extract-urls.js output/assets/styles/mobile-native-chat.css >> output/embedded-resources.txt
./build-scripts/copy-embedded-resources.sh
embedded-chat-copy: embedded-chat
rm -rf $(EMBEDDED_WWW_DIRECTORY)
mkdir -p $(EMBEDDED_WWW_DIRECTORY)
cp -R output/embedded/www/* $(EMBEDDED_WWW_DIRECTORY)/
# make-jquery:
# npm install
# ./node_modules/.bin/jquery-builder -v 2.0.3 -e deprecated -m > public/repo/jquery/jquery.js
#!/bin/bash
while read i; do
dirname output/embedded/www/${i#output/assets/};
done < "output/embedded-resources.txt" | uniq | while read i; do
mkdir -p $i
done
for i in `cat output/embedded-resources.txt`; do
cp $i output/embedded/www/${i#output/assets/};
done
#!/usr/bin/env node
"use strict";
var postcss = require('postcss');
......@@ -6,90 +5,82 @@ var fs = require('fs');
var path = require('path');
var reduceFunctionCall = require('reduce-function-call');
var url = require('url');
var shimPositionOption = require('../scripts/yargs-shim-position-option');
var opts = require('yargs')
.option('input', shimPositionOption({
position: 0,
required: true,
description: 'Output'
}))
.help('help')
.alias('help', 'h')
.argv;
function extractUrls(input, basePath) {
var resources = {};
var resources = {};
function getFileName(value) {
var u = url.parse(value);
if(u.protocol || u.hostname) return value;
function getFileName(value) {
var u = url.parse(value);
if(u.protocol || u.hostname) return value;
value = u.pathname;
value = u.pathname;
if(value.indexOf('.') === 0) {
var inputRelativeDir = path.dirname(path.relative(basePath, input));
var relativeToPublicFilePath = path.join(inputRelativeDir, value);
return path.join('public', relativeToPublicFilePath);
}
if(value.indexOf('.') === 0) {
var abs = path.resolve(path.dirname(opts.input), value);
return path.relative(process.cwd(), abs);
return value;
}
return value;
}
var css = postcss.parse(fs.readFileSync(opts.input));
var css = postcss.parse(fs.readFileSync(input));
css.eachDecl(function(decl) {
if (!decl.value) return;
css.eachDecl(function(decl) {
if (!decl.value) return;
if(decl.prop !== 'src' || decl.parent.name !== 'font-face') return;
if(decl.prop !== 'src' || decl.parent.name !== 'font-face') return;
if (!decl.parent.decls) return;
if (!decl.parent.decls) return;
var d = decl.parent.decls.filter(function(d) {
return d.prop === 'src' && d.parent.name === 'font-face';
});
var d = decl.parent.decls.filter(function(d) {
return d.prop === 'src' && d.parent.name === 'font-face';
});
if(!d.length) return;
if(!d.length) return;
// var hasTtf = d.filter(function(x) {
// return x.value.indexOf('.ttf') >= 0;
// });
// var hasTtf = d.filter(function(x) {
// return x.value.indexOf('.ttf') >= 0;
// });
// if(hasTtf.length) {
// decl.parent.decls = hasTtf;
// }
// if(hasTtf.length) {
// decl.parent.decls = hasTtf;
// }
});
});
css.eachDecl(function(decl) {
if (!decl.value) return;
css.eachDecl(function(decl) {
if (!decl.value) return;
var urls = [];
var urls = [];
reduceFunctionCall(decl.value, "url", function(value) {
var m = /^['"]([^'"]*)['"]$/.exec(value);
if(m) value = m[1];
reduceFunctionCall(decl.value, "url", function(value) {
var m = /^['"]([^'"]*)['"]$/.exec(value);
if(m) value = m[1];
value = getFileName(value);
value = getFileName(value);
urls.push(value);
});
if(decl.prop === 'src' && decl.parent.name === 'font-face') {
// If there are multiple fonts, choose the ttf
var ttfs = urls.filter(function(v) {
return (/\.ttf/).test(v);
urls.push(value);
});
if(ttfs.length > 0) {
urls = ttfs;
if(decl.prop === 'src' && decl.parent.name === 'font-face') {
// If there are multiple fonts, choose the ttf
var ttfs = urls.filter(function(v) {
return (/\.ttf/).test(v);
});
if(ttfs.length > 0) {
urls = ttfs;
}
}
}
urls.forEach(function(value) {
resources[value] = true;
urls.forEach(function(value) {
resources[value] = true;
});
});
});
return Object.keys(resources);
}
Object.keys(resources).forEach(function(key) {
console.log(key);
});
module.exports = extractUrls;
'use strict';
var gulp = require('gulp');
var postcss = require('gulp-postcss');
var autoprefixer = require('autoprefixer-core');
var mqpacker = require('css-mqpacker');
var csswring = require('csswring');
var styleBuilder = require('./style-builder');
var getSourceMapOptions = require('./get-sourcemap-options');
var webpack = require('webpack-stream');
var uglify = require('gulp-uglify');
const Promise = require('bluebird');
const path = require('path');
const gulp = require('gulp');
var cssDestDir = 'output/assets/styles';
var cssWatchGlob = 'public/**/*.less';
const postcss = require('gulp-postcss');
const autoprefixer = require('autoprefixer-core');
const mqpacker = require('css-mqpacker');
const csswring = require('csswring');
const styleBuilder = require('./style-builder');
const getSourceMapOptions = require('./get-sourcemap-options');
const webpack = require('webpack-stream');
const uglify = require('gulp-uglify');
const childProcessPromise = require('./child-process-promise');
const extractUrls = require('./extract-urls');
var opts = require('yargs')
.option('android', {
type: 'boolean',
default: false,
description: 'Output'
})
.option('ios', {
type: 'boolean',
default: false,
description: 'Output'
})
.help('help')
.alias('help', 'h')
.argv;
let buildPath;
if(opts.android) {
buildPath = 'output/android/www/';
}
else if(opts.ios) {
buildPath = 'output/ios/www/';
}
else {
throw new Error('Please define the --android of --ios args when running the embedded build')
}
/**
* Hook into the compile stage
*/
gulp.task('embedded:compile', ['clientapp:compile:copy-files', 'embedded:compile:css', 'embedded:compile:webpack']);
gulp.task('embedded:compile', [
'clientapp:compile:copy-files',
'embedded:compile:markup',
'embedded:compile:css',
'embedded:compile:webpack'
]);
// We also copy files after the CSS is compiled in `embedded:post-compile:copy-linked-assets`
gulp.task('clientapp:compile:copy-files', function() {
return gulp.src([
'public/images/emoji/*',
// 'public/sprites/**',
'public/repo/katex/**',
], {
base: './public',
stat: true
})
.pipe(gulp.dest(buildPath));
});
gulp.task('embedded:compile:markup', function() {
const args = [
path.join(__dirname, './render-embedded-chat.js'),
'--output', path.join(buildPath, 'mobile/embedded-chat.html'),
];
if(opts.android) {
args.push('--android');
}
if(opts.ios) {
args.push('--ios');
}
return childProcessPromise.spawn('node', args, Object.assign({}, process.env, {
// Default to prod config
NODE_ENV: process.env.NODE_ENV || 'prod'
}));
});
var cssIosStyleBuilder = styleBuilder([
const cssIosStyleBuilder = styleBuilder([
'public/less/mobile-native-chat.less'
], {
dest: cssDestDir,
watchGlob: cssWatchGlob,
dest: path.join(buildPath, 'styles'),
watchGlob: 'public/**/*.less',
sourceMapOptions: getSourceMapOptions(),
lessOptions: {
paths: ['public/less'],
......@@ -47,31 +111,37 @@ gulp.task('embedded:compile:css', function() {
return cssIosStyleBuilder.build();
});
/* Generate embedded native */
gulp.task('embedded:compile:webpack', ['clientapp:compile:copy-files'], function() {
return gulp.src('./public/js/webpack-mobile-native.config')
.pipe(webpack(require('../public/js/webpack-mobile-native.config')))
.pipe(gulp.dest('output/assets/js'));
.pipe(gulp.dest(path.join(buildPath, 'js')));
});
gulp.task('embedded:post-compile', ['embedded:post-compile:uglify']);
gulp.task('embedded:post-compile', [
'embedded:post-compile:uglify',
'embedded:post-compile:copy-linked-assets'
]);
gulp.task('embedded:post-compile:uglify', function() {
return gulp.src('output/assets/js/*.js')
return gulp.src(path.join(buildPath, 'js/*.js'))
.pipe(uglify())
.pipe(gulp.dest('output/assets/js'));
.pipe(gulp.dest(path.join(buildPath, 'js')));
});
gulp.task('clientapp:compile:copy-files', function() {
return gulp.src([
'public/fonts/**',
'public/images/**',
// 'public/sprites/**',
'public/repo/katex/**',
], {
base: "./public",
stat: true
gulp.task('embedded:post-compile:copy-linked-assets', function() {
return Promise.all([
extractUrls(path.join(buildPath, 'styles/mobile-native-chat.css'), buildPath),
])
.then((resourceLists) => {
const resourceList = resourceLists.reduce((list, resultantList) => {
return resultantList.concat(list);
}, []);
return gulp.src(resourceList, {
base: './public',
stat: true
})
.pipe(gulp.dest(buildPath));
})
.pipe(gulp.dest('output/assets'));
});
#!/usr/bin/env node
"use strict";
var express = require('express');
var fs = require('fs');
var expressHbs = require('express-hbs');
var resolveStatic = require('../server/web/resolve-static');
const Promise = require('bluebird');
const express = require('express');
const fs = require('fs-extra');
const outputFile = Promise.promisify(fs.outputFile);
const expressHbs = require('express-hbs');
const resolveStatic = require('../server/web/resolve-static');
require('../server/web/register-helpers')(expressHbs);
const shutdown = require('shutdown');
var opts = require('yargs')
.option('output', {
......@@ -12,6 +16,11 @@ var opts = require('yargs')
required: true,
description: 'Output'
})
.option('android', {
type: 'boolean',
default: false,
description: 'Output'
})
.help('help')
.alias('help', 'h')
.argv;
......@@ -21,8 +30,7 @@ function die(err) {
process.exit(1);
}
var app = express();
require('../server/web/register-helpers')(expressHbs);
const app = express();
app.engine('hbs', expressHbs.express3({
partialsDir: resolveStatic('/templates/partials'),
......@@ -36,9 +44,16 @@ app.engine('hbs', expressHbs.express3({
app.set('view engine', 'hbs');
app.set('views', resolveStatic('/templates'));
app.render('mobile/native-embedded-chat-app', {}, function(err, html) {
if(err) return die(err);
fs.writeFileSync(opts.output, html, { encoding: 'utf8' });
process.exit(0);
app.render('mobile/native-embedded-chat-app', {
isAndroidBuild: opts.android
}, function(err, html) {
if(err) {
die(err);
}
outputFile(opts.output, html, { encoding: 'utf8' })
.catch(die)
.then(() => {
shutdown.shutdownGracefully();
});
});
......@@ -31,6 +31,8 @@ for the ability to customize your username in the future and remove the suffix.
Sign out of Gitter and sign back in to update your avatar (or any other info).
Currently, there is an outstanding bug where this doesn't work with GitLab/Twitter accounts, https://gitlab.com/gitlab-org/gitter/webapp/issues/1834
## How do I delete my account?
......
......@@ -2,16 +2,23 @@
A community holds many rooms.
Communities are also referred to as "groups" internally.
Communities are also referred to as "groups" internally in the codebase.
## Community creation
Use the "+" button in the bottom-left of the menu bar to start the community creation process
Use the "+" button in the bottom-left of the menu bar to start the community creation process.
Communities can be associated with a GitHub organisation or repo. If you don't see the GitHub org/repo listed that you want to associate, see [*Why isn't my GitHub organisation or repos appearing?* FAQ](./faq.md#why-isnt-my-github-organisation-or-repos-appearing]
![](https://i.imgur.com/Wjk6Y4h.png)
## Community admins
You can add new admins for a room by visiting a room in the community -> **Room settings dropdown** -> **Permissions** modal -> **Edit community permissions**
## Rename a community
If you want to rename a community because a GitHub org/repo was renamed/transferred, see this [FAQ section instead](./faq.md#what-happens-if-i-rename-something-on-GitHub-org-repo) instead.
......
# Customization
## Dark theme
You can toggle the dark theme via the profile menu dropdown in the top-right -> **Toggle Dark Theme**
![](https://i.imgur.com/Bct6v63.png)
......@@ -6,6 +6,26 @@ Frequently asked questions.
Gitter is entirely free for all public and private conversations.
## I have a suggestion, where can I suggest/discuss it?
Feel free to come discuss your idea in the [`gitterHQ/gitter`](https://gitter.im/gitterHQ/gitter) room and create an issue, https://gitlab.com/gitlab-org/gitter/webapp/issues
## Help/Support!?!?!
If your support inquiry doesn't need to be private, drop by the [`gitterHQ/gitter`](https://gitter.im/gitterHQ/gitter) room.
Otheriwse, send a message to support@gitter.im
## Do you have an API?
Why, we are so gladded you asked, **yes** we do.
You can build apps on top of Gitter by using our REST API. You can find more information about it at [developer.gitter.im](https://developer.gitter.im/) or use our [Node.js module](https://www.npmjs.org/package/node-gitter).
If you have any questions about our API, please don't hesitate to contact us or join us in our [`gitterHQ/api`](https://gitter.im/gitterHQ/api) chat room.
## Can I merge/connect my accounts?
......@@ -27,6 +47,11 @@ See [Accounts](./accounts.md#how-do-i-remove-the-twitter-suffix-from-my-username
See [Accounts](./accounts.md#how-do-i-update-my-avatar)
## You want write access on my private repos? Are you insane?
See ["Private Repositories" on the OAuth scopes page](./oauth-scopes.md#private-repositories)
## Why isn't my GitHub organisation or repos appearing?
The first thing to check is [adding private repo access OAuth scope](./oauth-scopes.md#private-repositories) in order to see a private GitHub repo.
......@@ -46,10 +71,6 @@ You can also try making your organisation membership public, `https://github.com
For more information see [OAuth Scopes](./oauth-scopes.md).
## You want write access on my private repos? Are you insane?
See ["Private Repositories" on the OAuth scopes page](./oauth-scopes.md#private-repositories)
## What happens if I rename something on GitHub (org, repo)
#### Org rename
......@@ -87,3 +108,29 @@ Send a message to support@gitter.im with the following info. Make sure to email
- Link to where the repo now exists on GitHub
- Link to the old room on Gitter
- Link to the new room on Gitter
## What is Gitter Next?
Gitter Next is where we put our up-coming release branch up for preview. Most of the time it's small changes or even under-the-hood changes you don't see. Sometimes we put pretty large changes up there too and love to hear your feedback.
Sometimes it blows up, but not very often.
You can enable Gitter next in a variety of ways.
#### Desktop Browser
In your desktop browser, visit http://next.gitter.im and move the giant switchy thing over to next. Then refresh. You will notice a little green pill at the top of your screen that says "NEXT". You are now on Next.
There's also a bookmarklet at the bottom of the Next page and you can add this to your browser things to toggle this pretty easily.
#### Linux and Windows app
There's a convenient little toggle on the Gitter menu of your application to turn Next on or off.
![](https://i.imgur.com/QZJm2MM.png)
#### Mac app
**Gitter** menu bar dropdown in the top-left -> **Use Gitter Next**
![](https://i.imgur.com/YXtPn4N.png)
# Feature Toggles
Various features on Gitter are hidden behind a feature toggle. It is best to [enable Next](./faq.md#what-is-gitter-next) when using these features.
Some features are experimental and it is best not to just enable everything as they could easily break some things as they are developed.
## Using Gitter in your browser?
Simply visit [next.gitter.im](http://next.gitter.im/) and make sure the big giant Next switch is enabled, then find the left-menu toggle further down the screen and enable it. Refresh Gitter and you should be good to go.
![](https://i.imgur.com/Z4S0KX3.png)
## Windows or Linux App
If you use Gitter on Windows or Linux, it's a little fiddlier than that. Click on the Gitter menu in the top-bar and make sure Gitter Next is enabled. Then click on Developer Tools which will bring up a window and select the Console tab. Find the select box that defaults to <top frame> at the top of the console and change the value to mainframe. Once that's done, paste the following code into the console and press Enter.
```
document.cookie = 'fflip=' + encodeURIComponent('j:' + JSON.stringify({ 'some-feature-toggle': true })) + ';path=/;expires=' + new Date(Date.now() + 31536000000).toUTCString();
location.reload();
```
## Mac App
Make sure Enable Developer Tools is enabled from the Gitter menu in the top bar. Right click anywhere in the application and choose Inspect Element. This will bring up a web console, just paste the following code snippet into the console and press Enter. The app will refresh after a second or so.
```
document.cookie = 'fflip=' + encodeURIComponent('j:' + JSON.stringify({ 'some-feature-toggle': true })) + ';path=/;expires=' + new Date(Date.now() + 31536000000).toUTCString();
location.reload();
```
......@@ -6,10 +6,16 @@
- [FAQ](./faq.md)
- [Gitter accounts](./accounts.md)
- [Mobile apps (Android/iOS)](./mobile-apps.md)
- [Rooms](./rooms.md)
- [Desktop app](https://gitlab.com/gitlab-org/gitter/desktop/)
- [Keyboard shortcuts](./keyboard-shortcuts.md)
- [Customization](./customization.md)
- [Communities](./communities.md)
- [Rooms](./rooms.md)
- [Messages](./messages.md)
- [Notifications](./notifications.md)
- [OAuth Scopes](./oauth-scopes.md)
- [Integrations/Activity](./integrations.md)
- [Feature toggles](./feature-toggles.md)
## Developers
......
# Integrations/Activity
Gitter is integrated with numerous services via webhooks. When you add an integration to a chat room, you will see events from that integration appear in the right-toolbar under the "Activity" section.
![](https://i.imgur.com/nZZcCN3.png)
Our integrations include:
- GitLab
- GitHub
- BitBucket
- Trello
- AppVeyor
- CircleCI
- Codecov
- Codeship
- Coveralls
- Discourse
- Docker Hub
- Doorbell
- Drone
- GitBook
- GoCD
- Heroku
- Huboard
- Jenkins
- Logentries
- New Relic
- Open Collective
- PagerDuty
- Pivotal Tracker
- Sentry
- SnapCI
- Sprint.ly
- The Bug Geneie
- TrackJS
- Travis
- Send us a Merge request to add your own, https://gitlab.com/gitlab-org/gitter/services
## Setup
**Room settings dropdown** -> **Integrations**
![](https://i.imgur.com/AI4pBBP.png)
# Keyboard shortcuts
You can find a list of our shortcuts with any of the following methods,
- Click the `M↓` icon to the right of the chat input area then click the **Keyboard shortuts** button at the bottom
- `?` general keyboard shortcut
- `Cmd + Ctrl + k` keyboard shortcut on macOS
- `Ctrl + Shift + k` keyboard shortcut on Windows.
# Dark arts of the left menu
The left menu lists all of your conversations. It also contains a few cunningly awesome features.
Firstly, understanding how the left menu is ordered is important. Any chat rooms or people that have been **favourited** will show first, followed by chat rooms where there are **unread notifications** and finally other chat rooms ordered by the time you last accessed them.
So that's basically:
- Favourites
- Unread notifications
- Recents
If you click and drag any room below **favourites**, you can move it up into the favourites list. You can also explicitly order favourites by dragging them around.
Items below the favourites cannot be ordered as they are ordered by unread or recent activity.
You can also drag a room out of favourites. This will remove it from your favourites and it will automatically be positioned in the list below based on when it was last accessed as per all of the other recent rooms.
## Hiding rooms
It is also possible to hide a room from this list. If you receive a notification for the room again, it will reappear. If you no longer wish to receive notifications from that room, you should either leave that room (`/leave` slash command) or change notifications to mentions only. You cannot hide a one to one room.
![](https://i.imgur.com/1TkEWol.png)
# Messages
All rooms have unlimited message history, public or private.
## Writing messages
By default, hitting Enter when a chat message is typed will send the message, **Chat mode**. If you want to write multi-line messages, you can either insert a line break manually (Shift + Enter). Alternatively, you can toggle **Compose mode** where Enter will add a line break without sending a message (Ctrl + Enter will send the message).
There is also toggle button that can be found to the right of the chat input area and will look like the following two icons depending on which mode is activated.
Chat Mode | Compose Mode
--- | ---
![](https://i.imgur.com/nmLvAJo.png) | ![](https://i.imgur.com/yUGHhwV.png)
Additionally, if you type three backticks followed by Enter, we will automagically toggle compose mode (as well as close your backticks for you) so you can easily type in code. When you send the message, compose mode will get toggled back off. Neat, huh?
#### Keyboard shortcuts
`Ctrl + /`: Toggle Normal/Compose Mode
**Normal Mode**
- `Enter` - to send message
- `Shift + Enter` - to go to new line
**Compose Mode**
- `Enter` - to go to new line
- `Shift + Enter` - to go to new line (behaves same as `Enter`)
- `Ctrl + Enter` - to send message
## Message syntax
### Markdown
This is probably one of the reasons you are here in the first place. Gitter supports markdown in chat. Yes, that's right. Markdown. In Chat. Such win.
We not only support basic markdown, but also we do syntax highlighting for code and also support issue mentions and @ mentions.