pin chats
merge set avatar button and avatar
This commit is contained in:
parent
deae55c0d1
commit
70a71d4212
3 changed files with 142 additions and 23 deletions
|
|
@ -30,6 +30,13 @@
|
|||
width: 56px;
|
||||
height: 56px;
|
||||
margin: 0 auto .5rem auto;
|
||||
cursor: pointer;
|
||||
transition: filter .15s, transform .15s;
|
||||
}
|
||||
|
||||
[xmpp_Folders_pane] [xmpp_My_avatar]:hover {
|
||||
filter: brightness(.7);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
[xmpp_Folders_spacer] {
|
||||
|
|
@ -127,6 +134,18 @@
|
|||
opacity: 1;
|
||||
}
|
||||
|
||||
[xmpp_Roster_contact] {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
[xmpp_Roster_contact_drag] {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
[xmpp_Roster_contact_link] {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
|
@ -144,6 +163,32 @@
|
|||
opacity: .4;
|
||||
}
|
||||
|
||||
[xmpp_Roster_contact_drop][mol_drop_status="drag"] [xmpp_Roster_contact_link] {
|
||||
background: var(--mol_theme_focus, rgba(74,158,255,.15));
|
||||
}
|
||||
|
||||
[xmpp_Pin_button] {
|
||||
flex-shrink: 0;
|
||||
width: 1.8rem;
|
||||
min-width: 1.8rem;
|
||||
padding: 0;
|
||||
opacity: 0;
|
||||
transition: opacity .15s;
|
||||
}
|
||||
|
||||
[xmpp_Roster_contact]:hover [xmpp_Pin_button],
|
||||
[xmpp_Roster_contact][xmpp_pinned="true"] [xmpp_Pin_button] {
|
||||
opacity: .6;
|
||||
}
|
||||
|
||||
[xmpp_Pin_button]:hover {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
[xmpp_Roster_contact][xmpp_pinned="true"] [xmpp_Pin_button] {
|
||||
color: var(--mol_theme_focus, #4a9eff);
|
||||
}
|
||||
|
||||
[xmpp_Roster_pane] {
|
||||
width: 320px;
|
||||
flex-shrink: 0;
|
||||
|
|
@ -190,7 +235,6 @@
|
|||
}
|
||||
|
||||
[xmpp_Folders_pane] [xmpp_Disconnect_button],
|
||||
[xmpp_Folders_pane] [xmpp_Set_avatar_button],
|
||||
[xmpp_Folders_pane] [xmpp_Lights2] {
|
||||
min-height: 2rem;
|
||||
padding: .35rem;
|
||||
|
|
|
|||
|
|
@ -44,6 +44,9 @@ $xmpp $mol_view
|
|||
^
|
||||
src <= my_avatar_uri \
|
||||
alt \
|
||||
event *
|
||||
^
|
||||
click? <=> do_set_avatar? null
|
||||
<= Folder_all $mol_link
|
||||
arg *
|
||||
folder null
|
||||
|
|
@ -68,11 +71,6 @@ $xmpp $mol_view
|
|||
sub /
|
||||
<= new_folder_text @ \+ Drop chat
|
||||
<= Folders_spacer $mol_view
|
||||
<= Set_avatar_button $mol_button_minor
|
||||
hint @ \Set avatar
|
||||
click? <=> do_set_avatar? null
|
||||
sub /
|
||||
<= Set_avatar_icon $mol_icon_camera
|
||||
<= Lights2 $mol_lights_toggle
|
||||
<= Disconnect_button $mol_button_minor
|
||||
hint <= disconnect_label @ \Disconnect
|
||||
|
|
@ -170,22 +168,34 @@ $xmpp $mol_view
|
|||
title <= send_label @ \Send
|
||||
click? <=> do_send*? null
|
||||
-
|
||||
Roster_contact* $mol_drag
|
||||
transfer *
|
||||
text/plain <= roster_jid* \
|
||||
Sub <= Roster_contact_link* $mol_link
|
||||
arg *
|
||||
chat <= roster_jid* \
|
||||
sub /
|
||||
<= Contact_avatar* $mol_view
|
||||
dom_name \img
|
||||
attr *
|
||||
^
|
||||
src <= contact_avatar_uri* \
|
||||
alt \
|
||||
<= Contact_label* $mol_view
|
||||
sub /
|
||||
<= contact_display* \
|
||||
Roster_contact* $mol_view
|
||||
attr *
|
||||
^
|
||||
xmpp_pinned <= contact_pinned* false
|
||||
sub /
|
||||
<= Roster_contact_drag* $mol_drag
|
||||
transfer *
|
||||
text/plain <= roster_jid* \
|
||||
Sub <= Roster_contact_drop* $mol_drop
|
||||
adopt?transfer <=> folder_adopt?transfer null
|
||||
receive?obj <=> reorder_pin*?obj null
|
||||
Sub <= Roster_contact_link* $mol_link
|
||||
arg *
|
||||
chat <= roster_jid* \
|
||||
sub /
|
||||
<= Contact_avatar* $mol_view
|
||||
dom_name \img
|
||||
attr *
|
||||
^
|
||||
src <= contact_avatar_uri* \
|
||||
alt \
|
||||
<= Contact_label* $mol_view
|
||||
sub /
|
||||
<= contact_display* \
|
||||
<= Pin_button* $mol_button_minor
|
||||
click? <=> toggle_pin*? null
|
||||
sub /
|
||||
<= Pin_icon* $mol_icon_pin
|
||||
-
|
||||
Msg* $mol_view
|
||||
sub /
|
||||
|
|
|
|||
67
xmpp.view.ts
67
xmpp.view.ts
|
|
@ -765,6 +765,66 @@ private _handle_message(el: Element) {
|
|||
@ $mol_mem
|
||||
folders_ver(next?: number) { return next ?? 0 }
|
||||
|
||||
// ── Pins per folder ─────────────────────────────────────────────────
|
||||
|
||||
@ $mol_mem
|
||||
pins_ver(next?: number) { return next ?? 0 }
|
||||
|
||||
// Folder key used in pin storage. Empty/null URL arg → 'all'.
|
||||
private _folder_key(): string {
|
||||
const f = this.folder()
|
||||
return f ? f : 'all'
|
||||
}
|
||||
|
||||
private _pins_obj(): Record<string, string[]> {
|
||||
this.pins_ver()
|
||||
return (this.$.$mol_state_local.value('xmpp_pins') as Record<string, string[]> | null) ?? {}
|
||||
}
|
||||
|
||||
private _save_pins(obj: Record<string, string[]>) {
|
||||
this.$.$mol_state_local.value('xmpp_pins', obj)
|
||||
this.pins_ver(this.pins_ver() + 1)
|
||||
}
|
||||
|
||||
pinned_jids(folder: string): string[] {
|
||||
return this._pins_obj()[folder] ?? []
|
||||
}
|
||||
|
||||
contact_pinned(jid: string): boolean {
|
||||
return this.pinned_jids(this._folder_key()).includes(jid)
|
||||
}
|
||||
|
||||
toggle_pin(jid: string) {
|
||||
const folder = this._folder_key()
|
||||
const obj = { ...this._pins_obj() }
|
||||
const arr = [...(obj[folder] ?? [])]
|
||||
const idx = arr.indexOf(jid)
|
||||
if (idx >= 0) arr.splice(idx, 1)
|
||||
else arr.unshift(jid)
|
||||
if (arr.length === 0) delete obj[folder]
|
||||
else obj[folder] = arr
|
||||
this._save_pins(obj)
|
||||
}
|
||||
|
||||
// Drop handler on a contact row: reorder pins. `target_jid` is this row's jid (anchor),
|
||||
// `source_jid` is the dragged contact. Insert source before target in the pin list.
|
||||
reorder_pin(target_jid: string, source_jid: string) {
|
||||
if (!source_jid || target_jid === source_jid) return
|
||||
const folder = this._folder_key()
|
||||
const obj = { ...this._pins_obj() }
|
||||
let arr = [...(obj[folder] ?? [])].filter(j => j !== source_jid)
|
||||
const idx = arr.indexOf(target_jid)
|
||||
if (idx >= 0) {
|
||||
arr.splice(idx, 0, source_jid)
|
||||
} else {
|
||||
// Target not pinned → pin it first, then place source above it.
|
||||
arr.push(target_jid)
|
||||
arr.unshift(source_jid)
|
||||
}
|
||||
obj[folder] = arr
|
||||
this._save_pins(obj)
|
||||
}
|
||||
|
||||
private _folders_obj(): Record<string, string[]> {
|
||||
return (this.$.$mol_state_local.value('xmpp_folders') as Record<string, string[]> | null) ?? {}
|
||||
}
|
||||
|
|
@ -1059,6 +1119,7 @@ private _handle_message(el: Element) {
|
|||
@ $mol_mem
|
||||
roster_rows() {
|
||||
this.folders_ver()
|
||||
this.pins_ver()
|
||||
const folder = this.folder()
|
||||
const contacts = this.contacts()
|
||||
const rooms = this.rooms()
|
||||
|
|
@ -1076,7 +1137,11 @@ private _handle_message(el: Element) {
|
|||
return jid.toLowerCase().includes(q) || name.includes(q)
|
||||
})
|
||||
}
|
||||
return jids.map(j => this.Roster_contact(j))
|
||||
// Pinned at top in pin order, rest below preserving original order.
|
||||
const pinned_set = new Set(this.pinned_jids(this._folder_key()))
|
||||
const pinned = this.pinned_jids(this._folder_key()).filter(j => jids.includes(j))
|
||||
const rest = jids.filter(j => !pinned_set.has(j))
|
||||
return [...pinned, ...rest].map(j => this.Roster_contact(j))
|
||||
}
|
||||
|
||||
// Suggestion buttons appear when the search query looks like a JID and isn't already in the list.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue