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;
|
width: 56px;
|
||||||
height: 56px;
|
height: 56px;
|
||||||
margin: 0 auto .5rem auto;
|
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] {
|
[xmpp_Folders_spacer] {
|
||||||
|
|
@ -127,6 +134,18 @@
|
||||||
opacity: 1;
|
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] {
|
[xmpp_Roster_contact_link] {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
@ -144,6 +163,32 @@
|
||||||
opacity: .4;
|
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] {
|
[xmpp_Roster_pane] {
|
||||||
width: 320px;
|
width: 320px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
|
@ -190,7 +235,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
[xmpp_Folders_pane] [xmpp_Disconnect_button],
|
[xmpp_Folders_pane] [xmpp_Disconnect_button],
|
||||||
[xmpp_Folders_pane] [xmpp_Set_avatar_button],
|
|
||||||
[xmpp_Folders_pane] [xmpp_Lights2] {
|
[xmpp_Folders_pane] [xmpp_Lights2] {
|
||||||
min-height: 2rem;
|
min-height: 2rem;
|
||||||
padding: .35rem;
|
padding: .35rem;
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,9 @@ $xmpp $mol_view
|
||||||
^
|
^
|
||||||
src <= my_avatar_uri \
|
src <= my_avatar_uri \
|
||||||
alt \
|
alt \
|
||||||
|
event *
|
||||||
|
^
|
||||||
|
click? <=> do_set_avatar? null
|
||||||
<= Folder_all $mol_link
|
<= Folder_all $mol_link
|
||||||
arg *
|
arg *
|
||||||
folder null
|
folder null
|
||||||
|
|
@ -68,11 +71,6 @@ $xmpp $mol_view
|
||||||
sub /
|
sub /
|
||||||
<= new_folder_text @ \+ Drop chat
|
<= new_folder_text @ \+ Drop chat
|
||||||
<= Folders_spacer $mol_view
|
<= 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
|
<= Lights2 $mol_lights_toggle
|
||||||
<= Disconnect_button $mol_button_minor
|
<= Disconnect_button $mol_button_minor
|
||||||
hint <= disconnect_label @ \Disconnect
|
hint <= disconnect_label @ \Disconnect
|
||||||
|
|
@ -170,9 +168,17 @@ $xmpp $mol_view
|
||||||
title <= send_label @ \Send
|
title <= send_label @ \Send
|
||||||
click? <=> do_send*? null
|
click? <=> do_send*? null
|
||||||
-
|
-
|
||||||
Roster_contact* $mol_drag
|
Roster_contact* $mol_view
|
||||||
|
attr *
|
||||||
|
^
|
||||||
|
xmpp_pinned <= contact_pinned* false
|
||||||
|
sub /
|
||||||
|
<= Roster_contact_drag* $mol_drag
|
||||||
transfer *
|
transfer *
|
||||||
text/plain <= roster_jid* \
|
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
|
Sub <= Roster_contact_link* $mol_link
|
||||||
arg *
|
arg *
|
||||||
chat <= roster_jid* \
|
chat <= roster_jid* \
|
||||||
|
|
@ -186,6 +192,10 @@ $xmpp $mol_view
|
||||||
<= Contact_label* $mol_view
|
<= Contact_label* $mol_view
|
||||||
sub /
|
sub /
|
||||||
<= contact_display* \
|
<= contact_display* \
|
||||||
|
<= Pin_button* $mol_button_minor
|
||||||
|
click? <=> toggle_pin*? null
|
||||||
|
sub /
|
||||||
|
<= Pin_icon* $mol_icon_pin
|
||||||
-
|
-
|
||||||
Msg* $mol_view
|
Msg* $mol_view
|
||||||
sub /
|
sub /
|
||||||
|
|
|
||||||
67
xmpp.view.ts
67
xmpp.view.ts
|
|
@ -765,6 +765,66 @@ private _handle_message(el: Element) {
|
||||||
@ $mol_mem
|
@ $mol_mem
|
||||||
folders_ver(next?: number) { return next ?? 0 }
|
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[]> {
|
private _folders_obj(): Record<string, string[]> {
|
||||||
return (this.$.$mol_state_local.value('xmpp_folders') as Record<string, string[]> | null) ?? {}
|
return (this.$.$mol_state_local.value('xmpp_folders') as Record<string, string[]> | null) ?? {}
|
||||||
}
|
}
|
||||||
|
|
@ -1059,6 +1119,7 @@ private _handle_message(el: Element) {
|
||||||
@ $mol_mem
|
@ $mol_mem
|
||||||
roster_rows() {
|
roster_rows() {
|
||||||
this.folders_ver()
|
this.folders_ver()
|
||||||
|
this.pins_ver()
|
||||||
const folder = this.folder()
|
const folder = this.folder()
|
||||||
const contacts = this.contacts()
|
const contacts = this.contacts()
|
||||||
const rooms = this.rooms()
|
const rooms = this.rooms()
|
||||||
|
|
@ -1076,7 +1137,11 @@ private _handle_message(el: Element) {
|
||||||
return jid.toLowerCase().includes(q) || name.includes(q)
|
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.
|
// 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