mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +09:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12417a6942 | ||
|
|
d05f6c9aaa | ||
|
|
0ca035c547 | ||
|
|
dfd46ccc2c |
No files matched your search
@@ -336,6 +336,9 @@ jobs:
|
||||
restore-keys: |
|
||||
rebar3-${{ runner.os }}-otp28-rebar3.27.0-
|
||||
|
||||
- name: Drop restored gateway build output
|
||||
run: rm -rf fluxer_gateway/_build/default/lib/fluxer_gateway fluxer_gateway/_build/test/lib/fluxer_gateway
|
||||
|
||||
- name: Check formatting
|
||||
run: |
|
||||
"$FLUXER_CI_BIN" ci --step gateway_fmt
|
||||
|
||||
@@ -588,7 +588,12 @@ const ComposerInner = ({
|
||||
if (disabledRef.current) {
|
||||
return false;
|
||||
}
|
||||
selectLastSelectionOrEnd();
|
||||
const selection = $getSelection();
|
||||
if ($isRangeSelection(selection)) {
|
||||
$setSelection(selection.clone());
|
||||
} else {
|
||||
selectLastSelectionOrEnd();
|
||||
}
|
||||
return false;
|
||||
},
|
||||
COMMAND_PRIORITY_LOW,
|
||||
|
||||
@@ -1077,9 +1077,15 @@ deliver_after_grace(Params, Sessions, RecheckAt, Presences) ->
|
||||
ok = apply_push_worker_priority(),
|
||||
LiveSessions = await_grace_recheck(monitor_grace_sessions(Sessions), RecheckAt),
|
||||
send_push_now(
|
||||
users_left_offline(lists:usort(maps:values(LiveSessions)), Presences), Params
|
||||
grace_recipients(maps:keys(Sessions), maps:values(LiveSessions), Presences), Params
|
||||
).
|
||||
|
||||
-spec grace_recipients([user_id()], [user_id()], term()) -> [user_id()].
|
||||
grace_recipients(HeldUserIds, LiveUserIds, Presences) ->
|
||||
Live = lists:usort(LiveUserIds),
|
||||
Gone = lists:usort(HeldUserIds) -- Live,
|
||||
lists:usort(Gone ++ users_left_offline(Live, Presences)).
|
||||
|
||||
-spec monitor_grace_sessions(grace_sessions()) -> #{reference() => user_id()}.
|
||||
monitor_grace_sessions(Sessions) ->
|
||||
maps:fold(fun monitor_user_sessions/3, #{}, Sessions).
|
||||
@@ -1315,6 +1321,57 @@ presence_eligibility_allows_when_presence_table_missing_test() ->
|
||||
build_push_presence_eligibility(#{<<"s1">> => #{user_id => 1}}, #{})
|
||||
).
|
||||
|
||||
grace_recipients_push_a_user_whose_held_sessions_all_ended_test() ->
|
||||
Presences = #{1 => #{<<"status">> => <<"online">>}},
|
||||
?assertEqual([1], grace_recipients([1], [], Presences)).
|
||||
|
||||
grace_recipients_skip_a_user_still_online_on_a_held_session_test() ->
|
||||
Presences = #{1 => #{<<"status">> => <<"online">>}},
|
||||
?assertEqual([], grace_recipients([1], [1], Presences)).
|
||||
|
||||
grace_recipients_push_a_user_whose_live_session_went_offline_test() ->
|
||||
Presences = #{1 => #{<<"status">> => <<"offline">>}},
|
||||
?assertEqual([1], grace_recipients([1], [1], Presences)).
|
||||
|
||||
grace_recipients_skip_a_user_with_one_ended_and_one_live_online_session_test() ->
|
||||
Presences = #{1 => #{<<"status">> => <<"online">>}},
|
||||
?assertEqual([], grace_recipients([1], [1], Presences)).
|
||||
|
||||
grace_recipients_mix_users_test() ->
|
||||
Presences = #{
|
||||
1 => #{<<"status">> => <<"online">>},
|
||||
2 => #{<<"status">> => <<"online">>},
|
||||
3 => #{<<"status">> => <<"offline">>}
|
||||
},
|
||||
?assertEqual([1, 3], grace_recipients([1, 2, 3], [2, 3, 3], Presences)).
|
||||
|
||||
deliver_after_grace_pushes_a_user_whose_session_exits_inside_the_window_test() ->
|
||||
Self = self(),
|
||||
ok = meck:new(push, [passthrough, non_strict]),
|
||||
try
|
||||
ok = meck:expect(push, handle_message_create, fun(Params) ->
|
||||
Self ! {pushed, maps:get(user_ids, Params)},
|
||||
ok
|
||||
end),
|
||||
Session = spawn(fun() ->
|
||||
receive
|
||||
stop -> ok
|
||||
end
|
||||
end),
|
||||
Held = #{42 => [Session]},
|
||||
Presences = #{42 => #{<<"status">> => <<"online">>}},
|
||||
RecheckAt = erlang:monotonic_time(millisecond) + 300,
|
||||
_ = spawn(fun() -> deliver_after_grace(#{}, Held, RecheckAt, Presences) end),
|
||||
timer:sleep(50),
|
||||
Session ! stop,
|
||||
receive
|
||||
{pushed, UserIds} -> ?assertEqual([42], UserIds)
|
||||
after 2000 -> erlang:error(no_push_after_session_exit)
|
||||
end
|
||||
after
|
||||
meck:unload(push)
|
||||
end.
|
||||
|
||||
legacy_session_eligibility_suppresses_every_real_session_test() ->
|
||||
RealSession = #{
|
||||
session_id => <<"s1">>,
|
||||
|
||||
@@ -2382,8 +2382,6 @@ fn decode_postgres_message(row: serde_json::Value) -> anyhow::Result<Message> {
|
||||
.get("pinned_timestamp")
|
||||
.is_some_and(|value| !value.is_null());
|
||||
row.insert("pinned".to_owned(), serde_json::Value::Bool(pinned));
|
||||
default_i32_field(&mut row, "type", 0);
|
||||
default_i32_field(&mut row, "version", 0);
|
||||
Ok(serde_json::from_value(serde_json::Value::Object(row))?)
|
||||
}
|
||||
|
||||
@@ -2415,16 +2413,6 @@ fn decode_postgres_attachment_decay(
|
||||
Ok((row.attachment_id, expires_at))
|
||||
}
|
||||
|
||||
fn default_i32_field(
|
||||
row: &mut serde_json::Map<String, serde_json::Value>,
|
||||
field: &str,
|
||||
value: i32,
|
||||
) {
|
||||
if row.get(field).is_none_or(serde_json::Value::is_null) {
|
||||
row.insert(field.to_owned(), serde_json::Value::Number(value.into()));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "scylla")]
|
||||
fn rows_to_messages(rows: QueryRowsResult) -> anyhow::Result<Vec<Message>> {
|
||||
let rows: Vec<MessageDbRow> = rows.rows::<MessageDbRow>()?.collect::<Result<_, _>>()?;
|
||||
@@ -3404,6 +3392,36 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_responses_request_accepts_legacy_null_version_rows() {
|
||||
let request: MessageRequest = serde_json::from_value(json!({
|
||||
"op": "BuildResponses",
|
||||
"messages": [{
|
||||
"message_id": "1449544529132171273",
|
||||
"channel_id": "1431572375251247158",
|
||||
"bucket": 399,
|
||||
"author_id": "1130650140672000000",
|
||||
"type": null,
|
||||
"version": null,
|
||||
"content": ""
|
||||
}],
|
||||
"viewer_user_id": "1130650140672000000",
|
||||
"source_guild_id": null,
|
||||
"message_history_cutoff_ms": null,
|
||||
"can_read_message_history": true,
|
||||
"media_endpoint": "https://media.example",
|
||||
"media_proxy_secret_key": "secret",
|
||||
"include_reactions": true
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
let MessageRequest::BuildResponses { messages, .. } = request else {
|
||||
panic!("expected BuildResponses");
|
||||
};
|
||||
assert_eq!(messages[0].message_type, 0);
|
||||
assert_eq!(messages[0].version, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mention_context_carries_embed_user_ids_for_message_and_snapshots() {
|
||||
let message: Message = serde_json::from_value(json!({
|
||||
|
||||
@@ -58,6 +58,13 @@ mod serde_id {
|
||||
.map(StringOrI64::into_i64)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn i32_or_null_as_default<'de, D>(deserializer: D) -> Result<i32, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Ok(Option::<i32>::deserialize(deserializer)?.unwrap_or_default())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -164,7 +171,11 @@ pub struct Message {
|
||||
pub bucket: i32,
|
||||
#[serde(default, deserialize_with = "serde_id::opt_i64_from_string_or_number")]
|
||||
pub author_id: Option<i64>,
|
||||
#[serde(rename = "type")]
|
||||
#[serde(
|
||||
rename = "type",
|
||||
default,
|
||||
deserialize_with = "serde_id::i32_or_null_as_default"
|
||||
)]
|
||||
pub message_type: i32,
|
||||
#[serde(default, deserialize_with = "serde_id::opt_i64_from_string_or_number")]
|
||||
pub webhook_id: Option<i64>,
|
||||
@@ -191,6 +202,7 @@ pub struct Message {
|
||||
)]
|
||||
pub mention_channels: Vec<i64>,
|
||||
pub has_reaction: Option<bool>,
|
||||
#[serde(default, deserialize_with = "serde_id::i32_or_null_as_default")]
|
||||
pub version: i32,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub attachments: Option<Vec<MessageAttachment>>,
|
||||
|
||||
Reference in new issue
Block a user