Log direct messages and keep track of open direct message tabs
This commit is contained in:
parent
e97bb519ed
commit
8305dd561d
File diff suppressed because one or more lines are too long
@ -29,6 +29,13 @@ function loadState({ store }, env) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (env.openDMs) {
|
||||||
|
store.dispatch({
|
||||||
|
type: 'PRIVATE_CHATS',
|
||||||
|
privateChats: env.openDMs
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (env.users) {
|
if (env.users) {
|
||||||
store.dispatch({
|
store.dispatch({
|
||||||
type: socketActions.USERS,
|
type: socketActions.USERS,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import Cookie from 'js-cookie';
|
import Cookie from 'js-cookie';
|
||||||
import debounce from 'lodash/debounce';
|
import debounce from 'lodash/debounce';
|
||||||
import { getSelectedTab } from 'state/tab';
|
import { getSelectedTab } from 'state/tab';
|
||||||
import { isChannel, stringifyTab } from 'utils';
|
import { stringifyTab } from 'utils';
|
||||||
import { observe } from 'utils/observe';
|
import { observe } from 'utils/observe';
|
||||||
|
|
||||||
const saveTab = debounce(
|
const saveTab = debounce(
|
||||||
@ -11,7 +11,7 @@ const saveTab = debounce(
|
|||||||
|
|
||||||
export default function storage({ store }) {
|
export default function storage({ store }) {
|
||||||
observe(store, getSelectedTab, tab => {
|
observe(store, getSelectedTab, tab => {
|
||||||
if (isChannel(tab) || (tab.server && !tab.name)) {
|
if (tab.server) {
|
||||||
saveTab(tab);
|
saveTab(tab);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -26,6 +26,7 @@ export const CLOSE_MODAL = 'CLOSE_MODAL';
|
|||||||
|
|
||||||
export const CLOSE_PRIVATE_CHAT = 'CLOSE_PRIVATE_CHAT';
|
export const CLOSE_PRIVATE_CHAT = 'CLOSE_PRIVATE_CHAT';
|
||||||
export const OPEN_PRIVATE_CHAT = 'OPEN_PRIVATE_CHAT';
|
export const OPEN_PRIVATE_CHAT = 'OPEN_PRIVATE_CHAT';
|
||||||
|
export const PRIVATE_CHATS = 'PRIVATE_CHATS';
|
||||||
|
|
||||||
export const SEARCH_MESSAGES = 'SEARCH_MESSAGES';
|
export const SEARCH_MESSAGES = 'SEARCH_MESSAGES';
|
||||||
export const TOGGLE_SEARCH = 'TOGGLE_SEARCH';
|
export const TOGGLE_SEARCH = 'TOGGLE_SEARCH';
|
||||||
|
@ -30,6 +30,16 @@ export default createReducer(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
[actions.PRIVATE_CHATS](state, { privateChats }) {
|
||||||
|
privateChats.forEach(({ server, name }) => {
|
||||||
|
if (!state[server]) {
|
||||||
|
state[server] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
state[server].push(name);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
[actions.socket.PM](state, action) {
|
[actions.socket.PM](state, action) {
|
||||||
if (action.from.indexOf('.') === -1) {
|
if (action.from.indexOf('.') === -1) {
|
||||||
open(state, action.server, action.from);
|
open(state, action.server, action.from);
|
||||||
@ -46,7 +56,11 @@ export function openPrivateChat(server, nick) {
|
|||||||
return {
|
return {
|
||||||
type: actions.OPEN_PRIVATE_CHAT,
|
type: actions.OPEN_PRIVATE_CHAT,
|
||||||
server,
|
server,
|
||||||
nick
|
nick,
|
||||||
|
socket: {
|
||||||
|
type: 'open_dm',
|
||||||
|
data: { server, name: nick }
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +69,11 @@ export function closePrivateChat(server, nick) {
|
|||||||
dispatch({
|
dispatch({
|
||||||
type: actions.CLOSE_PRIVATE_CHAT,
|
type: actions.CLOSE_PRIVATE_CHAT,
|
||||||
server,
|
server,
|
||||||
nick
|
nick,
|
||||||
|
socket: {
|
||||||
|
type: 'close_dm',
|
||||||
|
data: { server, name: nick }
|
||||||
|
}
|
||||||
});
|
});
|
||||||
dispatch(updateSelection());
|
dispatch(updateSelection());
|
||||||
};
|
};
|
||||||
|
@ -31,6 +31,7 @@ type indexData struct {
|
|||||||
Defaults *config.Defaults
|
Defaults *config.Defaults
|
||||||
Servers []Server
|
Servers []Server
|
||||||
Channels []*storage.Channel
|
Channels []*storage.Channel
|
||||||
|
OpenDMs []storage.Tab
|
||||||
HexIP bool
|
HexIP bool
|
||||||
Version dispatchVersion
|
Version dispatchVersion
|
||||||
|
|
||||||
@ -43,7 +44,7 @@ type indexData struct {
|
|||||||
Messages *Messages
|
Messages *Messages
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Dispatch) getIndexData(r *http.Request, path string, state *State) *indexData {
|
func (d *Dispatch) getIndexData(r *http.Request, state *State) *indexData {
|
||||||
cfg := d.Config()
|
cfg := d.Config()
|
||||||
|
|
||||||
data := indexData{
|
data := indexData{
|
||||||
@ -98,35 +99,37 @@ func (d *Dispatch) getIndexData(r *http.Request, path string, state *State) *ind
|
|||||||
}
|
}
|
||||||
data.Channels = channels
|
data.Channels = channels
|
||||||
|
|
||||||
server, channel := getTabFromPath(path)
|
openDMs, err := state.user.GetOpenDMs()
|
||||||
if isInChannel(channels, server, channel) {
|
if err != nil {
|
||||||
data.addUsersAndMessages(server, channel, state)
|
return nil
|
||||||
return &data
|
|
||||||
}
|
}
|
||||||
|
data.OpenDMs = openDMs
|
||||||
|
|
||||||
server, channel = parseTabCookie(r, path)
|
tab, err := tabFromRequest(r)
|
||||||
if isInChannel(channels, server, channel) {
|
if err == nil && hasTab(channels, openDMs, tab.Server, tab.Name) {
|
||||||
data.addUsersAndMessages(server, channel, state)
|
data.addUsersAndMessages(tab.Server, tab.Name, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &data
|
return &data
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *indexData) addUsersAndMessages(server, channel string, state *State) {
|
func (d *indexData) addUsersAndMessages(server, name string, state *State) {
|
||||||
users := channelStore.GetUsers(server, channel)
|
if isChannel(name) {
|
||||||
|
users := channelStore.GetUsers(server, name)
|
||||||
if len(users) > 0 {
|
if len(users) > 0 {
|
||||||
d.Users = &Userlist{
|
d.Users = &Userlist{
|
||||||
Server: server,
|
Server: server,
|
||||||
Channel: channel,
|
Channel: name,
|
||||||
Users: users,
|
Users: users,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
messages, hasMore, err := state.user.GetLastMessages(server, channel, 50)
|
messages, hasMore, err := state.user.GetLastMessages(server, name, 50)
|
||||||
if err == nil && len(messages) > 0 {
|
if err == nil && len(messages) > 0 {
|
||||||
m := Messages{
|
m := Messages{
|
||||||
Server: server,
|
Server: server,
|
||||||
To: channel,
|
To: name,
|
||||||
Messages: messages,
|
Messages: messages,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,10 +141,16 @@ func (d *indexData) addUsersAndMessages(server, channel string, state *State) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func isInChannel(channels []*storage.Channel, server, channel string) bool {
|
func hasTab(channels []*storage.Channel, openDMs []storage.Tab, server, name string) bool {
|
||||||
if channel != "" {
|
if name != "" {
|
||||||
for _, ch := range channels {
|
for _, ch := range channels {
|
||||||
if server == ch.Server && channel == ch.Name {
|
if server == ch.Server && name == ch.Name {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tab := range openDMs {
|
||||||
|
if server == tab.Server && name == tab.Name {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -149,30 +158,52 @@ func isInChannel(channels []*storage.Channel, server, channel string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTabFromPath(rawPath string) (string, string) {
|
func tabFromRequest(r *http.Request) (Tab, error) {
|
||||||
path := strings.Split(strings.Trim(rawPath, "/"), "/")
|
tab := Tab{}
|
||||||
if len(path) >= 2 {
|
|
||||||
name, err := url.PathUnescape(path[len(path)-1])
|
var path string
|
||||||
if err == nil && isChannel(name) {
|
if strings.HasPrefix(r.URL.Path, "/ws") {
|
||||||
return path[len(path)-2], name
|
path = r.URL.EscapedPath()[3:]
|
||||||
}
|
} else {
|
||||||
}
|
referer, err := url.Parse(r.Referer())
|
||||||
return "", ""
|
if err != nil {
|
||||||
}
|
return tab, err
|
||||||
|
}
|
||||||
|
|
||||||
|
path = referer.EscapedPath()
|
||||||
|
}
|
||||||
|
|
||||||
func parseTabCookie(r *http.Request, path string) (string, string) {
|
|
||||||
if path == "/" {
|
if path == "/" {
|
||||||
cookie, err := r.Cookie("tab")
|
cookie, err := r.Cookie("tab")
|
||||||
if err == nil {
|
if err != nil {
|
||||||
v, err := url.PathUnescape(cookie.Value)
|
return tab, err
|
||||||
if err == nil {
|
}
|
||||||
tab := strings.SplitN(v, ";", 2)
|
|
||||||
|
|
||||||
if len(tab) == 2 && isChannel(tab[1]) {
|
v, err := url.PathUnescape(cookie.Value)
|
||||||
return tab[0], tab[1]
|
if err != nil {
|
||||||
|
return tab, err
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.SplitN(v, ";", 2)
|
||||||
|
if len(parts) == 2 {
|
||||||
|
tab.Server = parts[0]
|
||||||
|
tab.Name = parts[1]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parts := strings.Split(strings.Trim(path, "/"), "/")
|
||||||
|
if len(parts) > 0 && len(parts) < 3 {
|
||||||
|
if len(parts) == 2 {
|
||||||
|
name, err := url.PathUnescape(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
return tab, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tab.Name = name
|
||||||
|
}
|
||||||
|
|
||||||
|
tab.Server = parts[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
return tab, nil
|
||||||
return "", ""
|
|
||||||
}
|
}
|
||||||
|
@ -104,6 +104,29 @@ func easyjson7e607aefDecodeGithubComKhliengDispatchServer(in *jlexer.Lexer, out
|
|||||||
}
|
}
|
||||||
in.Delim(']')
|
in.Delim(']')
|
||||||
}
|
}
|
||||||
|
case "openDMs":
|
||||||
|
if in.IsNull() {
|
||||||
|
in.Skip()
|
||||||
|
out.OpenDMs = nil
|
||||||
|
} else {
|
||||||
|
in.Delim('[')
|
||||||
|
if out.OpenDMs == nil {
|
||||||
|
if !in.IsDelim(']') {
|
||||||
|
out.OpenDMs = make([]storage.Tab, 0, 2)
|
||||||
|
} else {
|
||||||
|
out.OpenDMs = []storage.Tab{}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out.OpenDMs = (out.OpenDMs)[:0]
|
||||||
|
}
|
||||||
|
for !in.IsDelim(']') {
|
||||||
|
var v3 storage.Tab
|
||||||
|
easyjson7e607aefDecodeGithubComKhliengDispatchStorage1(in, &v3)
|
||||||
|
out.OpenDMs = append(out.OpenDMs, v3)
|
||||||
|
in.WantComma()
|
||||||
|
}
|
||||||
|
in.Delim(']')
|
||||||
|
}
|
||||||
case "hexIP":
|
case "hexIP":
|
||||||
out.HexIP = bool(in.Bool())
|
out.HexIP = bool(in.Bool())
|
||||||
case "version":
|
case "version":
|
||||||
@ -176,11 +199,11 @@ func easyjson7e607aefEncodeGithubComKhliengDispatchServer(out *jwriter.Writer, i
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
out.RawByte('[')
|
out.RawByte('[')
|
||||||
for v3, v4 := range in.Servers {
|
for v4, v5 := range in.Servers {
|
||||||
if v3 > 0 {
|
if v4 > 0 {
|
||||||
out.RawByte(',')
|
out.RawByte(',')
|
||||||
}
|
}
|
||||||
out.Raw((v4).MarshalJSON())
|
out.Raw((v5).MarshalJSON())
|
||||||
}
|
}
|
||||||
out.RawByte(']')
|
out.RawByte(']')
|
||||||
}
|
}
|
||||||
@ -195,19 +218,38 @@ func easyjson7e607aefEncodeGithubComKhliengDispatchServer(out *jwriter.Writer, i
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
out.RawByte('[')
|
out.RawByte('[')
|
||||||
for v5, v6 := range in.Channels {
|
for v6, v7 := range in.Channels {
|
||||||
if v5 > 0 {
|
if v6 > 0 {
|
||||||
out.RawByte(',')
|
out.RawByte(',')
|
||||||
}
|
}
|
||||||
if v6 == nil {
|
if v7 == nil {
|
||||||
out.RawString("null")
|
out.RawString("null")
|
||||||
} else {
|
} else {
|
||||||
easyjson7e607aefEncodeGithubComKhliengDispatchStorage(out, *v6)
|
easyjson7e607aefEncodeGithubComKhliengDispatchStorage(out, *v7)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out.RawByte(']')
|
out.RawByte(']')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(in.OpenDMs) != 0 {
|
||||||
|
const prefix string = ",\"openDMs\":"
|
||||||
|
if first {
|
||||||
|
first = false
|
||||||
|
out.RawString(prefix[1:])
|
||||||
|
} else {
|
||||||
|
out.RawString(prefix)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
out.RawByte('[')
|
||||||
|
for v8, v9 := range in.OpenDMs {
|
||||||
|
if v8 > 0 {
|
||||||
|
out.RawByte(',')
|
||||||
|
}
|
||||||
|
easyjson7e607aefEncodeGithubComKhliengDispatchStorage1(out, v9)
|
||||||
|
}
|
||||||
|
out.RawByte(']')
|
||||||
|
}
|
||||||
|
}
|
||||||
if in.HexIP {
|
if in.HexIP {
|
||||||
const prefix string = ",\"hexIP\":"
|
const prefix string = ",\"hexIP\":"
|
||||||
if first {
|
if first {
|
||||||
@ -284,6 +326,61 @@ func (v *indexData) UnmarshalJSON(data []byte) error {
|
|||||||
func (v *indexData) UnmarshalEasyJSON(l *jlexer.Lexer) {
|
func (v *indexData) UnmarshalEasyJSON(l *jlexer.Lexer) {
|
||||||
easyjson7e607aefDecodeGithubComKhliengDispatchServer(l, v)
|
easyjson7e607aefDecodeGithubComKhliengDispatchServer(l, v)
|
||||||
}
|
}
|
||||||
|
func easyjson7e607aefDecodeGithubComKhliengDispatchStorage1(in *jlexer.Lexer, out *storage.Tab) {
|
||||||
|
isTopLevel := in.IsStart()
|
||||||
|
if in.IsNull() {
|
||||||
|
if isTopLevel {
|
||||||
|
in.Consumed()
|
||||||
|
}
|
||||||
|
in.Skip()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
in.Delim('{')
|
||||||
|
for !in.IsDelim('}') {
|
||||||
|
key := in.UnsafeFieldName(false)
|
||||||
|
in.WantColon()
|
||||||
|
if in.IsNull() {
|
||||||
|
in.Skip()
|
||||||
|
in.WantComma()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch key {
|
||||||
|
case "server":
|
||||||
|
out.Server = string(in.String())
|
||||||
|
case "name":
|
||||||
|
out.Name = string(in.String())
|
||||||
|
default:
|
||||||
|
in.SkipRecursive()
|
||||||
|
}
|
||||||
|
in.WantComma()
|
||||||
|
}
|
||||||
|
in.Delim('}')
|
||||||
|
if isTopLevel {
|
||||||
|
in.Consumed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func easyjson7e607aefEncodeGithubComKhliengDispatchStorage1(out *jwriter.Writer, in storage.Tab) {
|
||||||
|
out.RawByte('{')
|
||||||
|
first := true
|
||||||
|
_ = first
|
||||||
|
if in.Server != "" {
|
||||||
|
const prefix string = ",\"server\":"
|
||||||
|
first = false
|
||||||
|
out.RawString(prefix[1:])
|
||||||
|
out.String(string(in.Server))
|
||||||
|
}
|
||||||
|
if in.Name != "" {
|
||||||
|
const prefix string = ",\"name\":"
|
||||||
|
if first {
|
||||||
|
first = false
|
||||||
|
out.RawString(prefix[1:])
|
||||||
|
} else {
|
||||||
|
out.RawString(prefix)
|
||||||
|
}
|
||||||
|
out.String(string(in.Name))
|
||||||
|
}
|
||||||
|
out.RawByte('}')
|
||||||
|
}
|
||||||
func easyjson7e607aefDecodeGithubComKhliengDispatchStorage(in *jlexer.Lexer, out *storage.Channel) {
|
func easyjson7e607aefDecodeGithubComKhliengDispatchStorage(in *jlexer.Lexer, out *storage.Channel) {
|
||||||
isTopLevel := in.IsStart()
|
isTopLevel := in.IsStart()
|
||||||
if in.IsNull() {
|
if in.IsNull() {
|
||||||
@ -392,9 +489,9 @@ func easyjson7e607aefDecodeGithubComKhliengDispatchConfig(in *jlexer.Lexer, out
|
|||||||
out.Channels = (out.Channels)[:0]
|
out.Channels = (out.Channels)[:0]
|
||||||
}
|
}
|
||||||
for !in.IsDelim(']') {
|
for !in.IsDelim(']') {
|
||||||
var v7 string
|
var v10 string
|
||||||
v7 = string(in.String())
|
v10 = string(in.String())
|
||||||
out.Channels = append(out.Channels, v7)
|
out.Channels = append(out.Channels, v10)
|
||||||
in.WantComma()
|
in.WantComma()
|
||||||
}
|
}
|
||||||
in.Delim(']')
|
in.Delim(']')
|
||||||
@ -457,11 +554,11 @@ func easyjson7e607aefEncodeGithubComKhliengDispatchConfig(out *jwriter.Writer, i
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
out.RawByte('[')
|
out.RawByte('[')
|
||||||
for v8, v9 := range in.Channels {
|
for v11, v12 := range in.Channels {
|
||||||
if v8 > 0 {
|
if v11 > 0 {
|
||||||
out.RawByte(',')
|
out.RawByte(',')
|
||||||
}
|
}
|
||||||
out.String(string(v9))
|
out.String(string(v12))
|
||||||
}
|
}
|
||||||
out.RawByte(']')
|
out.RawByte(']')
|
||||||
}
|
}
|
||||||
@ -640,9 +737,9 @@ func easyjson7e607aefDecodeGithubComKhliengDispatchServer2(in *jlexer.Lexer, out
|
|||||||
out.Channels = (out.Channels)[:0]
|
out.Channels = (out.Channels)[:0]
|
||||||
}
|
}
|
||||||
for !in.IsDelim(']') {
|
for !in.IsDelim(']') {
|
||||||
var v10 string
|
var v13 string
|
||||||
v10 = string(in.String())
|
v13 = string(in.String())
|
||||||
out.Channels = append(out.Channels, v10)
|
out.Channels = append(out.Channels, v13)
|
||||||
in.WantComma()
|
in.WantComma()
|
||||||
}
|
}
|
||||||
in.Delim(']')
|
in.Delim(']')
|
||||||
@ -705,11 +802,11 @@ func easyjson7e607aefEncodeGithubComKhliengDispatchServer2(out *jwriter.Writer,
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
out.RawByte('[')
|
out.RawByte('[')
|
||||||
for v11, v12 := range in.Channels {
|
for v14, v15 := range in.Channels {
|
||||||
if v11 > 0 {
|
if v14 > 0 {
|
||||||
out.RawByte(',')
|
out.RawByte(',')
|
||||||
}
|
}
|
||||||
out.String(string(v12))
|
out.String(string(v15))
|
||||||
}
|
}
|
||||||
out.RawByte(']')
|
out.RawByte(']')
|
||||||
}
|
}
|
||||||
|
@ -1,47 +1,60 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/khlieng/dispatch/storage"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetTabFromPath(t *testing.T) {
|
func TestGetTabFromPath(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
input string
|
input *http.Request
|
||||||
expectedServer string
|
expectedTab Tab
|
||||||
expectedChannel string
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"/chat.freenode.net/%23r%2Fstuff%2F/",
|
&http.Request{
|
||||||
"chat.freenode.net",
|
URL: &url.URL{Path: "/init"},
|
||||||
"#r/stuff/",
|
Header: http.Header{"Referer": []string{"/chat.freenode.net/%23r%2Fstuff%2F"}},
|
||||||
|
},
|
||||||
|
Tab{storage.Tab{Server: "chat.freenode.net", Name: "#r/stuff/"}},
|
||||||
}, {
|
}, {
|
||||||
"/chat.freenode.net/%23r%2Fstuff%2F",
|
&http.Request{
|
||||||
"chat.freenode.net",
|
URL: &url.URL{Path: "/init"},
|
||||||
"#r/stuff/",
|
Header: http.Header{"Referer": []string{"/chat.freenode.net/%23r%2Fstuff"}},
|
||||||
|
},
|
||||||
|
Tab{storage.Tab{Server: "chat.freenode.net", Name: "#r/stuff"}},
|
||||||
}, {
|
}, {
|
||||||
"/chat.freenode.net/%23r%2Fstuff",
|
&http.Request{
|
||||||
"chat.freenode.net",
|
URL: &url.URL{Path: "/init"},
|
||||||
"#r/stuff",
|
Header: http.Header{"Referer": []string{"/chat.freenode.net/%23stuff"}},
|
||||||
|
},
|
||||||
|
Tab{storage.Tab{Server: "chat.freenode.net", Name: "#stuff"}},
|
||||||
}, {
|
}, {
|
||||||
"/chat.freenode.net/%23stuff",
|
&http.Request{
|
||||||
"chat.freenode.net",
|
URL: &url.URL{Path: "/init"},
|
||||||
"#stuff",
|
Header: http.Header{"Referer": []string{"/chat.freenode.net/stuff"}},
|
||||||
|
},
|
||||||
|
Tab{storage.Tab{Server: "chat.freenode.net", Name: "stuff"}},
|
||||||
}, {
|
}, {
|
||||||
"/chat.freenode.net/%23stuff/cake",
|
&http.Request{
|
||||||
"",
|
URL: &url.URL{Path: "/init"},
|
||||||
"",
|
Header: http.Header{"Referer": []string{"/data/chat.freenode.net/%23apples"}},
|
||||||
|
},
|
||||||
|
Tab{},
|
||||||
}, {
|
}, {
|
||||||
"/data/chat.freenode.net/%23apples",
|
&http.Request{
|
||||||
"chat.freenode.net",
|
URL: &url.URL{Path: "/ws/chat.freenode.net"},
|
||||||
"#apples",
|
},
|
||||||
|
Tab{storage.Tab{Server: "chat.freenode.net"}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
server, channel := getTabFromPath(tc.input)
|
tab, err := tabFromRequest(tc.input)
|
||||||
assert.Equal(t, tc.expectedServer, server)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, tc.expectedChannel, channel)
|
assert.Equal(t, tc.expectedTab, tab)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -169,17 +169,21 @@ func (i *ircHandler) message(msg *irc.Message) {
|
|||||||
From: msg.Nick,
|
From: msg.Nick,
|
||||||
Content: msg.LastParam(),
|
Content: msg.LastParam(),
|
||||||
}
|
}
|
||||||
|
target := msg.Params[0]
|
||||||
|
|
||||||
if msg.Params[0] == i.client.GetNick() {
|
if target == i.client.GetNick() {
|
||||||
i.state.sendJSON("pm", message)
|
i.state.sendJSON("pm", message)
|
||||||
|
i.state.user.AddOpenDM(i.client.Host, message.From)
|
||||||
|
|
||||||
|
target = message.From
|
||||||
} else {
|
} else {
|
||||||
message.To = msg.Params[0]
|
message.To = target
|
||||||
i.state.sendJSON("message", message)
|
i.state.sendJSON("message", message)
|
||||||
}
|
}
|
||||||
|
|
||||||
if msg.Params[0] != "*" {
|
if target != "*" {
|
||||||
go i.state.user.LogMessage(message.ID,
|
go i.state.user.LogMessage(message.ID,
|
||||||
i.client.Host, msg.Nick, msg.Params[0], msg.LastParam())
|
i.client.Host, msg.Nick, target, msg.LastParam())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,3 +221,7 @@ type ChannelForward struct {
|
|||||||
Old string
|
Old string
|
||||||
New string
|
New string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Tab struct {
|
||||||
|
storage.Tab
|
||||||
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,6 @@ package server
|
|||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@ -163,14 +162,8 @@ func (d *Dispatch) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if r.URL.Path == "/init" {
|
if r.URL.Path == "/init" {
|
||||||
referer, err := url.Parse(r.Header.Get("Referer"))
|
|
||||||
if err != nil {
|
|
||||||
fail(w, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
state := d.handleAuth(w, r, true, true)
|
state := d.handleAuth(w, r, true, true)
|
||||||
data := d.getIndexData(r, referer.EscapedPath(), state)
|
data := d.getIndexData(r, state)
|
||||||
|
|
||||||
writeJSON(w, r, data)
|
writeJSON(w, r, data)
|
||||||
} else if strings.HasPrefix(r.URL.Path, "/ws") {
|
} else if strings.HasPrefix(r.URL.Path, "/ws") {
|
||||||
|
@ -68,17 +68,15 @@ func (h *wsHandler) init(r *http.Request) {
|
|||||||
h.state.numIRC(), "IRC connections |",
|
h.state.numIRC(), "IRC connections |",
|
||||||
h.state.numWS(), "WebSocket connections")
|
h.state.numWS(), "WebSocket connections")
|
||||||
|
|
||||||
|
tab, err := tabFromRequest(r)
|
||||||
|
|
||||||
channels, err := h.state.user.GetChannels()
|
channels, err := h.state.user.GetChannels()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
}
|
}
|
||||||
path := r.URL.EscapedPath()
|
|
||||||
pathServer, pathChannel := getTabFromPath(path)
|
|
||||||
cookieServer, cookieChannel := parseTabCookie(r, path[3:])
|
|
||||||
|
|
||||||
for _, channel := range channels {
|
for _, channel := range channels {
|
||||||
if (channel.Server == pathServer && channel.Name == pathChannel) ||
|
if channel.Server == tab.Server && channel.Name == tab.Name {
|
||||||
(channel.Server == cookieServer && channel.Name == cookieChannel) {
|
|
||||||
// Userlist and messages for this channel gets embedded in the index page
|
// Userlist and messages for this channel gets embedded in the index page
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -91,6 +89,19 @@ func (h *wsHandler) init(r *http.Request) {
|
|||||||
|
|
||||||
h.state.sendLastMessages(channel.Server, channel.Name, 50)
|
h.state.sendLastMessages(channel.Server, channel.Name, 50)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openDMs, err := h.state.user.GetOpenDMs()
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, openDM := range openDMs {
|
||||||
|
if openDM.Server == tab.Server && openDM.Name == tab.Name {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
h.state.sendLastMessages(openDM.Server, openDM.Name, 50)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *wsHandler) connect(b []byte) {
|
func (h *wsHandler) connect(b []byte) {
|
||||||
@ -306,6 +317,21 @@ func (h *wsHandler) channelSearch(b []byte) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *wsHandler) openDM(b []byte) {
|
||||||
|
var data Tab
|
||||||
|
data.UnmarshalJSON(b)
|
||||||
|
|
||||||
|
h.state.sendLastMessages(data.Server, data.Name, 50)
|
||||||
|
h.state.user.AddOpenDM(data.Server, data.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *wsHandler) closeDM(b []byte) {
|
||||||
|
var data Tab
|
||||||
|
data.UnmarshalJSON(b)
|
||||||
|
|
||||||
|
h.state.user.RemoveOpenDM(data.Server, data.Name)
|
||||||
|
}
|
||||||
|
|
||||||
func (h *wsHandler) initHandlers() {
|
func (h *wsHandler) initHandlers() {
|
||||||
h.handlers = map[string]func([]byte){
|
h.handlers = map[string]func([]byte){
|
||||||
"connect": h.connect,
|
"connect": h.connect,
|
||||||
@ -327,6 +353,8 @@ func (h *wsHandler) initHandlers() {
|
|||||||
"set_server_name": h.setServerName,
|
"set_server_name": h.setServerName,
|
||||||
"settings_set": h.setSettings,
|
"settings_set": h.setSettings,
|
||||||
"channel_search": h.channelSearch,
|
"channel_search": h.channelSearch,
|
||||||
|
"open_dm": h.openDM,
|
||||||
|
"close_dm": h.closeDM,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ var (
|
|||||||
bucketUsers = []byte("Users")
|
bucketUsers = []byte("Users")
|
||||||
bucketServers = []byte("Servers")
|
bucketServers = []byte("Servers")
|
||||||
bucketChannels = []byte("Channels")
|
bucketChannels = []byte("Channels")
|
||||||
|
bucketOpenDMs = []byte("OpenDMs")
|
||||||
bucketMessages = []byte("Messages")
|
bucketMessages = []byte("Messages")
|
||||||
bucketSessions = []byte("Sessions")
|
bucketSessions = []byte("Sessions")
|
||||||
)
|
)
|
||||||
@ -34,6 +35,7 @@ func New(path string) (*BoltStore, error) {
|
|||||||
tx.CreateBucketIfNotExists(bucketUsers)
|
tx.CreateBucketIfNotExists(bucketUsers)
|
||||||
tx.CreateBucketIfNotExists(bucketServers)
|
tx.CreateBucketIfNotExists(bucketServers)
|
||||||
tx.CreateBucketIfNotExists(bucketChannels)
|
tx.CreateBucketIfNotExists(bucketChannels)
|
||||||
|
tx.CreateBucketIfNotExists(bucketOpenDMs)
|
||||||
tx.CreateBucketIfNotExists(bucketMessages)
|
tx.CreateBucketIfNotExists(bucketMessages)
|
||||||
tx.CreateBucketIfNotExists(bucketSessions)
|
tx.CreateBucketIfNotExists(bucketSessions)
|
||||||
return nil
|
return nil
|
||||||
@ -168,6 +170,13 @@ func (s *BoltStore) RemoveServer(user *storage.User, address string) error {
|
|||||||
b.Delete(k)
|
b.Delete(k)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
b = tx.Bucket(bucketOpenDMs)
|
||||||
|
c = b.Cursor()
|
||||||
|
|
||||||
|
for k, _ := c.Seek(serverID); bytes.HasPrefix(k, serverID); k, _ = c.Next() {
|
||||||
|
b.Delete(k)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -246,6 +255,42 @@ func (s *BoltStore) RemoveChannel(user *storage.User, server, channel string) er
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *BoltStore) GetOpenDMs(user *storage.User) ([]storage.Tab, error) {
|
||||||
|
var openDMs []storage.Tab
|
||||||
|
|
||||||
|
s.db.View(func(tx *bolt.Tx) error {
|
||||||
|
c := tx.Bucket(bucketOpenDMs).Cursor()
|
||||||
|
|
||||||
|
for k, _ := c.Seek(user.IDBytes); bytes.HasPrefix(k, user.IDBytes); k, _ = c.Next() {
|
||||||
|
tab := bytes.Split(k[8:], []byte{0})
|
||||||
|
openDMs = append(openDMs, storage.Tab{
|
||||||
|
Server: string(tab[0]),
|
||||||
|
Name: string(tab[1]),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return openDMs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BoltStore) AddOpenDM(user *storage.User, server, nick string) error {
|
||||||
|
return s.db.Batch(func(tx *bolt.Tx) error {
|
||||||
|
b := tx.Bucket(bucketOpenDMs)
|
||||||
|
|
||||||
|
return b.Put(channelID(user, server, nick), nil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BoltStore) RemoveOpenDM(user *storage.User, server, nick string) error {
|
||||||
|
return s.db.Batch(func(tx *bolt.Tx) error {
|
||||||
|
b := tx.Bucket(bucketOpenDMs)
|
||||||
|
|
||||||
|
return b.Delete(channelID(user, server, nick))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (s *BoltStore) LogMessage(message *storage.Message) error {
|
func (s *BoltStore) LogMessage(message *storage.Message) error {
|
||||||
return s.db.Batch(func(tx *bolt.Tx) error {
|
return s.db.Batch(func(tx *bolt.Tx) error {
|
||||||
b, err := tx.Bucket(bucketMessages).CreateBucketIfNotExists([]byte(message.Server + ":" + message.To))
|
b, err := tx.Bucket(bucketMessages).CreateBucketIfNotExists([]byte(message.Server + ":" + message.To))
|
||||||
|
@ -38,6 +38,10 @@ type Store interface {
|
|||||||
GetChannels(user *User) ([]*Channel, error)
|
GetChannels(user *User) ([]*Channel, error)
|
||||||
AddChannel(user *User, channel *Channel) error
|
AddChannel(user *User, channel *Channel) error
|
||||||
RemoveChannel(user *User, server, channel string) error
|
RemoveChannel(user *User, server, channel string) error
|
||||||
|
|
||||||
|
GetOpenDMs(user *User) ([]Tab, error)
|
||||||
|
AddOpenDM(user *User, server, nick string) error
|
||||||
|
RemoveOpenDM(user *User, server, nick string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type SessionStore interface {
|
type SessionStore interface {
|
||||||
|
@ -190,6 +190,23 @@ func (u *User) RemoveChannel(server, channel string) error {
|
|||||||
return u.store.RemoveChannel(u, server, channel)
|
return u.store.RemoveChannel(u, server, channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Tab struct {
|
||||||
|
Server string
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) GetOpenDMs() ([]Tab, error) {
|
||||||
|
return u.store.GetOpenDMs(u)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) AddOpenDM(server, nick string) error {
|
||||||
|
return u.store.AddOpenDM(u, server, nick)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) RemoveOpenDM(server, nick string) error {
|
||||||
|
return u.store.RemoveOpenDM(u, server, nick)
|
||||||
|
}
|
||||||
|
|
||||||
type Message struct {
|
type Message struct {
|
||||||
ID string `json:"-" bleve:"-"`
|
ID string `json:"-" bleve:"-"`
|
||||||
Server string `json:"-" bleve:"server"`
|
Server string `json:"-" bleve:"server"`
|
||||||
|
@ -80,6 +80,16 @@ func TestUser(t *testing.T) {
|
|||||||
channels, err = user.GetChannels()
|
channels, err = user.GetChannels()
|
||||||
assert.Len(t, channels, 0)
|
assert.Len(t, channels, 0)
|
||||||
|
|
||||||
|
user.AddOpenDM(srv.Host, "cake")
|
||||||
|
openDMs, err := user.GetOpenDMs()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Len(t, openDMs, 1)
|
||||||
|
err = user.RemoveOpenDM(srv.Host, "cake")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
openDMs, err = user.GetOpenDMs()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Len(t, openDMs, 0)
|
||||||
|
|
||||||
settings := user.GetClientSettings()
|
settings := user.GetClientSettings()
|
||||||
assert.NotNil(t, settings)
|
assert.NotNil(t, settings)
|
||||||
assert.Equal(t, storage.DefaultClientSettings(), settings)
|
assert.Equal(t, storage.DefaultClientSettings(), settings)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user