Add favoriting/pinning channels

This commit is contained in:
Max Leiter 2022-05-01 00:37:21 -07:00
parent f2a8d5aacc
commit a8935376a1
No known key found for this signature in database
GPG key ID: A3512F2F2F17EBDA
11 changed files with 86 additions and 63 deletions

View file

@ -15,13 +15,27 @@
>
<span class="parted-channel-icon" />
</span>
<span class="close-tooltip tooltipped tooltipped-w" aria-label="Leave">
<button class="close" aria-label="Leave" @click.stop="close" />
<span
class="close-tooltip tooltipped tooltipped-w"
:aria-label="channel.favorite ? 'Unfavorite' : 'Leave'"
>
<button
class="close"
:aria-label="channel.favorite ? 'Unfavorite' : 'Leave'"
@click.stop="close"
/>
</span>
</template>
<template v-else>
<span class="close-tooltip tooltipped tooltipped-w" aria-label="Close">
<button class="close" aria-label="Close" @click.stop="close" />
<span
class="close-tooltip tooltipped tooltipped-w"
:aria-label="channel.favorite ? 'Unfavorite' : 'Close'"
>
<button
class="close"
:aria-label="channel.favorite ? 'Unfavorite' : 'Close'"
@click.stop="close"
/>
</span>
</template>
</ChannelWrapper>

View file

@ -57,6 +57,10 @@ export default {
const extra = [];
const type = this.channel.type;
if (this.channel.favorite) {
`favorited on ${this.network.name}`;
}
if (this.channel.unread > 0) {
if (this.channel.unread > 1) {
extra.push(`${this.channel.unread} unread messages`);

View file

@ -13,13 +13,13 @@
:id="'chan-' + channel.id"
class="chat-view"
:data-type="channel.type"
:aria-label="channel.name"
:aria-label="channel.displayName ? channel.displayName : channel.name"
role="tabpanel"
>
<div class="header">
<SidebarToggle />
<span class="title" :aria-label="'Currently open ' + channel.type">{{
channel.name
channel.displayName ? channel.displayName : channel.name
}}</span>
<div v-if="channel.editTopic === true" class="topic-container">
<input

View file

@ -4,31 +4,39 @@
<div class="lobby-wrap">
<CollapseFavoritesButton :on-collapse-click="onCollapseClick" />
<span title="Favorites" class="name">Favorites</span>
<span v-if="unreadCount > 0" class="badge">{{ unreadCount }}</span>
</div>
</div>
<div v-for="channel in $store.state.favoriteChannels" :key="channel.id">
<Channel
:channel="channel"
:network="network"
:is-filtering="false"
:active="
$store.state.activeChannel && channel === $store.state.activeChannel.channel
"
/>
</div>
<Draggable
draggable=".channel-list-item"
ghost-class="ui-sortable-ghost"
drag-class="ui-sortable-dragging"
:group="network.uuid"
:list="channels"
:delay="longTouchDuration"
:delay-on-touch-only="true"
:touch-start-threshold="10"
class="channels"
@choose="onDraggableChoose"
@unchoose="onDraggableUnchoose"
>
<template v-for="channel in channels">
<Channel
:key="channel.id"
:channel="channel"
:network="network"
:is-filtering="false"
:active="
$store.state.activeChannel && channel === $store.state.activeChannel.channel
"
/>
</template>
</Draggable>
</div>
</template>
<style scoped>
.lobby-wrap {
display: flex;
/* margin-left: 40px; */
}
</style>
<script>
import Draggable from "vuedraggable";
import eventbus from "../js/eventbus";
import roundBadgeNumber from "../js/helpers/roundBadgeNumber";
import Channel from "./Channel.vue";
import CollapseFavoritesButton from "./CollapseFavoritesButton.vue";
@ -37,9 +45,13 @@ export default {
components: {
Channel,
CollapseFavoritesButton,
Draggable,
},
props: {
channels: Array,
onDraggableUnchoose: Function,
onDraggableChoose: Function,
longTouchDuration: Number,
},
computed: {
network() {
@ -52,7 +64,6 @@ export default {
};
},
},
methods: {
onCollapseClick() {
this.$store.commit("toggleFavorites");
@ -63,13 +74,6 @@ export default {
channel: this.channel,
});
},
unreadCount() {
const unread = this.channels.reduce((acc, channel) => {
return acc + channel.unread || 0;
}, 0);
return roundBadgeNumber(unread);
},
},
};
</script>

View file

@ -80,7 +80,12 @@
role="region"
aria-live="polite"
>
<Favorites :channels="$store.state.favoriteChannels" />
<Favorites
:channels="$store.state.favoriteChannels"
:long-touch-duration="LONG_TOUCH_DURATION"
:on-draggable-unchoose="onDraggableUnchoose"
:on-draggable-choose="onDraggableChoose"
/>
</div>
<div
v-for="network in $store.state.networks"
@ -277,8 +282,6 @@ export default {
Mousetrap.bind("alt+shift+right", this.expandNetwork);
Mousetrap.bind("alt+shift+left", this.collapseNetwork);
Mousetrap.bind("alt+j", this.toggleSearch);
console.log(this.$store.state.favoriteChannels[0]);
},
beforeDestroy() {
Mousetrap.unbind("alt+shift+right", this.expandNetwork);

View file

@ -372,6 +372,8 @@ p {
.context-menu-clear-favorites::before,
.context-menu-clear-history::before { content: "\f1f8"; /* https://fontawesome.com/icons/trash?style=solid */ }
.context-menu-mute::before { content: "\f6a9"; /* https://fontawesome.com/v5.15/icons/volume-mute?style=solid */ }
.context-menu-favorite::before { content: "\f005"; /* http://fontawesome.io/icon/star/ */ }
.channel-list-item .not-secure-icon::before {
content: "\f071"; /* https://fontawesome.com/icons/exclamation-triangle?style=solid */
@ -510,6 +512,7 @@ p {
color: #2ecc40;
}
#chat .msg[data-type="action"] .from::before {
content: "\f005"; /* http://fontawesome.io/icon/star/ */
}

View file

@ -97,9 +97,9 @@ export function generateChannelContextMenu($root, channel, network) {
class: "favorite",
action() {
if (channel.favorite) {
socket.emit("favorites:remove", channel.id);
socket.emit("favorites:remove", Number(channel.id));
} else {
socket.emit("favorites:add", channel.id);
socket.emit("favorites:add", Number(channel.id));
}
},
});
@ -210,7 +210,6 @@ export function generateChannelContextMenu($root, channel, network) {
});
}
// Add close menu item
items.push({
label: closeMap[channel.type],
type: "item",

View file

@ -4,7 +4,5 @@ import socket from "../socket";
import store from "../store";
socket.on("favorites", function (data) {
console.log("favorites", data);
store.commit("favoriteChannels", data.favoriteChannels);
});

View file

@ -132,7 +132,6 @@ const store = new Vuex.Store({
state.messageSearchResults = value;
},
favoriteChannels(state, payload) {
console.log("payload", payload);
state.favoriteChannels.forEach((channel) => {
channel.favorite = false;
channel.displayName = "";
@ -159,7 +158,7 @@ const store = new Vuex.Store({
);
netChan.channel.displayName =
netChan.channel.name + `(${netChan.network.name})`;
netChan.channel.name + ` (${netChan.network.name})`;
otherNetChan.channel.displayName =
otherNetChan.channel.name + ` (${otherNetChan.network.name})`;

View file

@ -50,16 +50,16 @@ new Vue({
});
}
);
} else if (channel.favorite) {
socket.emit("favorites:remove", Number(channel.id));
} else {
channel.closed = true;
return;
socket.emit("input", {
target: Number(channel.id),
text: "/close",
});
}
channel.closed = true;
socket.emit("input", {
target: Number(channel.id),
text: "/close",
});
},
},
render(createElement) {

View file

@ -120,18 +120,7 @@ function Client(manager, name, config = {}) {
}
});
(client.config.networks || []).forEach((network) => {
client.connect(network, true);
for (const chan of network.channels) {
if (chan.favorite) {
// third argument is whether to save or not;
// we don't need to here as the config is loaded from the filesystem
console.log(network.uuid, chan.id);
client.addToFavorites(network.uuid, chan.id);
}
}
});
(client.config.networks || []).forEach((network) => client.connect(network, true));
// Networks are stored directly in the client object
// We don't need to keep it in the config object
@ -215,6 +204,7 @@ Client.prototype.connect = function (args, isStartup = false) {
key: chan.key || "",
type: chan.type,
muted: chan.muted,
favorite: chan.favorite,
})
);
});
@ -308,6 +298,15 @@ Client.prototype.connect = function (args, isStartup = false) {
client.save();
channels.forEach((channel) => channel.loadMessages(client, network));
}
channels.forEach((chan) => {
if (chan.favorite) {
// The third argument for addToFavorites is whether to save,
// we will only be adding in this case if the favorite is loaded from disk,
// so we can safely set it to false.
this.addToFavorites(network.uuid, chan.id, false);
}
});
};
Client.prototype.generateToken = function (callback) {
@ -666,7 +665,7 @@ Client.prototype.part = function (network, chan) {
const client = this;
network.channels = _.without(network.channels, chan);
client.mentions = client.mentions.filter((msg) => !(msg.chanId === chan.id));
client.favoriteChannels = client.favoriteChannels.filter((fav) => fav.id !== chan.id);
client.favoriteChannels = client.favoriteChannels.filter((fav) => fav.channelId !== chan.id);
chan.destroy();
client.save();
client.emit("part", {