feat: Support fetching accepted Activity Center notifications by multiple types (#3088)

Summary
=======

- [x] Changes endpoint ActivityCenterNotificationsBy to support fetching
  multiple types of notification in a single query.
- [x] Adds endpoint UnreadAndAcceptedActivityCenterNotificationsCount to
  allow the mobile client to fetch the count of unread & accepted
  notifications.
- [x] Add `golangci-lint` to Nix shell. This was possible since PR
  https://github.com/status-im/status-go/pull/3087 was merged.

Notes
=====

- If you'd like to understand why these changes are needed, please see
  the mobile PR https://github.com/status-im/status-mobile/pull/14785,
  or issue https://github.com/status-im/status-mobile/issues/14712
- All changes should be completely backwards compatible, and there
  should be no impact for the desktop app.
- The mobile client has been already tested using this branch.
This commit is contained in:
Icaro Motta 2023-01-20 09:45:32 -03:00 committed by GitHub
parent f9faac4293
commit e40cbfc28f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 252 additions and 145 deletions

View File

@ -1 +1 @@
0.124.0
0.125.0

View File

@ -37,7 +37,6 @@ func (db sqlitePersistence) DeleteActivityCenterNotificationForMessage(chatID st
params := activityCenterQueryParams{
chatID: chatID,
activityCenterType: ActivityCenterNotificationNoType,
}
_, notifications, err := db.buildActivityCenterQuery(tx, params)
@ -300,21 +299,23 @@ type activityCenterQueryParams struct {
chatID string
author string
read ActivityCenterQueryParamsRead
activityCenterType ActivityCenterType
accepted bool
activityCenterTypes []ActivityCenterType
}
func (db sqlitePersistence) buildActivityCenterQuery(tx *sql.Tx, params activityCenterQueryParams) (string, []*ActivityCenterNotification, error) {
var args []interface{}
var cursorWhere, inQueryWhere, inChatWhere, fromAuthorWhere, ofTypeWhere, readWhere string
var cursorWhere, inQueryWhere, inChatWhere, fromAuthorWhere, inTypeWhere, readWhere, acceptedWhere string
cursor := params.cursor
ids := params.ids
author := params.author
activityCenterType := params.activityCenterType
activityCenterTypes := params.activityCenterTypes
limit := params.limit
chatID := params.chatID
read := params.read
accepted := params.accepted
if cursor != "" {
cursorWhere = "AND cursor <= ?" //nolint: goconst
@ -322,13 +323,11 @@ func (db sqlitePersistence) buildActivityCenterQuery(tx *sql.Tx, params activity
}
if len(ids) != 0 {
inVector := strings.Repeat("?, ", len(ids)-1) + "?"
inQueryWhere = fmt.Sprintf(" AND a.id IN (%s)", inVector)
for _, id := range ids {
args = append(args, id)
}
}
switch read {
@ -338,20 +337,27 @@ func (db sqlitePersistence) buildActivityCenterQuery(tx *sql.Tx, params activity
readWhere = "AND NOT(a.read)"
}
if !accepted {
acceptedWhere = "AND NOT a.accepted"
}
if chatID != "" {
inChatWhere = "AND a.chat_id = ?" //nolint: goconst
args = append(args, chatID)
}
if author != "" {
fromAuthorWhere = " AND author = ?"
fromAuthorWhere = " AND a.author = ?"
args = append(args, author)
}
if activityCenterType != ActivityCenterNotificationNoType {
ofTypeWhere = " AND notification_type = ?"
if len(activityCenterTypes) != 0 {
inVector := strings.Repeat("?, ", len(activityCenterTypes)-1) + "?"
inTypeWhere = fmt.Sprintf(" AND a.notification_type IN (%s)", inVector)
for _, activityCenterType := range activityCenterTypes {
args = append(args, activityCenterType)
}
}
query := fmt.Sprintf( // nolint: gosec
`
@ -376,14 +382,15 @@ func (db sqlitePersistence) buildActivityCenterQuery(tx *sql.Tx, params activity
LEFT JOIN chats c
ON
c.id = a.chat_id
WHERE NOT a.dismissed AND NOT a.accepted
WHERE NOT a.dismissed
%s
%s
%s
%s
%s
%s
ORDER BY cursor DESC`, cursorWhere, inQueryWhere, inChatWhere, fromAuthorWhere, ofTypeWhere, readWhere)
%s
ORDER BY cursor DESC`, cursorWhere, inQueryWhere, inChatWhere, fromAuthorWhere, inTypeWhere, readWhere, acceptedWhere)
if limit != 0 {
args = append(args, limit)
@ -504,9 +511,9 @@ func (db sqlitePersistence) GetActivityCenterNotificationByID(id types.HexBytes)
return notification, err
}
func (db sqlitePersistence) UnreadActivityCenterNotifications(cursor string, limit uint64, activityType ActivityCenterType) (string, []*ActivityCenterNotification, error) {
func (db sqlitePersistence) UnreadActivityCenterNotifications(cursor string, limit uint64, activityTypes []ActivityCenterType) (string, []*ActivityCenterNotification, error) {
params := activityCenterQueryParams{
activityCenterType: activityType,
activityCenterTypes: activityTypes,
cursor: cursor,
limit: limit,
read: ActivityCenterQueryParamsReadUnread,
@ -515,9 +522,9 @@ func (db sqlitePersistence) UnreadActivityCenterNotifications(cursor string, lim
return db.activityCenterNotifications(params)
}
func (db sqlitePersistence) ReadActivityCenterNotifications(cursor string, limit uint64, activityType ActivityCenterType) (string, []*ActivityCenterNotification, error) {
func (db sqlitePersistence) ReadActivityCenterNotifications(cursor string, limit uint64, activityTypes []ActivityCenterType) (string, []*ActivityCenterNotification, error) {
params := activityCenterQueryParams{
activityCenterType: activityType,
activityCenterTypes: activityTypes,
cursor: cursor,
limit: limit,
read: ActivityCenterQueryParamsReadRead,
@ -526,12 +533,13 @@ func (db sqlitePersistence) ReadActivityCenterNotifications(cursor string, limit
return db.activityCenterNotifications(params)
}
func (db sqlitePersistence) ActivityCenterNotificationsBy(cursor string, limit uint64, activityType ActivityCenterType, readType ActivityCenterQueryParamsRead) (string, []*ActivityCenterNotification, error) {
func (db sqlitePersistence) ActivityCenterNotificationsBy(cursor string, limit uint64, activityTypes []ActivityCenterType, readType ActivityCenterQueryParamsRead, accepted bool) (string, []*ActivityCenterNotification, error) {
params := activityCenterQueryParams{
activityCenterType: activityType,
activityCenterTypes: activityTypes,
cursor: cursor,
limit: limit,
read: readType,
accepted: accepted,
}
return db.activityCenterNotifications(params)
@ -657,11 +665,7 @@ func (db sqlitePersistence) AcceptAllActivityCenterNotifications() ([]*ActivityC
_ = tx.Rollback()
}()
params := activityCenterQueryParams{
activityCenterType: ActivityCenterNotificationNoType,
}
_, notifications, err := db.buildActivityCenterQuery(tx, params)
_, notifications, err := db.buildActivityCenterQuery(tx, activityCenterQueryParams{})
_, err = tx.Exec(`UPDATE activity_center_notifications SET read = 1, accepted = 1 WHERE NOT accepted AND NOT dismissed`)
if err != nil {
@ -690,7 +694,6 @@ func (db sqlitePersistence) AcceptActivityCenterNotifications(ids []types.HexByt
params := activityCenterQueryParams{
ids: ids,
activityCenterType: ActivityCenterNotificationNoType,
}
_, notifications, err := db.buildActivityCenterQuery(tx, params)
@ -762,7 +765,7 @@ func (db sqlitePersistence) AcceptActivityCenterNotificationsForInvitesFromUser(
params := activityCenterQueryParams{
author: userPublicKey,
activityCenterType: ActivityCenterNotificationTypeNewPrivateGroupChat,
activityCenterTypes: []ActivityCenterType{ActivityCenterNotificationTypeNewPrivateGroupChat},
}
_, notifications, err := db.buildActivityCenterQuery(tx, params)
@ -813,9 +816,27 @@ func (db sqlitePersistence) MarkActivityCenterNotificationsUnread(ids []types.He
}
func buildActivityCenterNotificationsCountQuery(isAccepted bool) string {
var acceptedWhere string
if !isAccepted {
acceptedWhere = `AND NOT accepted`
}
return fmt.Sprintf(`SELECT COUNT(1) FROM activity_center_notifications WHERE NOT read AND NOT dismissed %s`, acceptedWhere)
}
func (db sqlitePersistence) UnreadActivityCenterNotificationsCount() (uint64, error) {
var count uint64
err := db.db.QueryRow(`SELECT COUNT(1) FROM activity_center_notifications WHERE NOT read AND NOT dismissed AND NOT accepted`).Scan(&count)
query := buildActivityCenterNotificationsCountQuery(false)
err := db.db.QueryRow(query).Scan(&count)
return count, err
}
func (db sqlitePersistence) UnreadAndAcceptedActivityCenterNotificationsCount() (uint64, error) {
var count uint64
query := buildActivityCenterNotificationsCountQuery(true)
err := db.db.QueryRow(query).Scan(&count)
return count, err
}

View File

@ -16,6 +16,10 @@ func (m *Messenger) UnreadActivityCenterNotificationsCount() (uint64, error) {
return m.persistence.UnreadActivityCenterNotificationsCount()
}
func (m *Messenger) UnreadAndAcceptedActivityCenterNotificationsCount() (uint64, error) {
return m.persistence.UnreadAndAcceptedActivityCenterNotificationsCount()
}
func toHexBytes(b [][]byte) []types.HexBytes {
hb := make([]types.HexBytes, len(b))
@ -243,8 +247,8 @@ func (m *Messenger) ActivityCenterNotifications(cursor string, limit uint64) (*A
}, nil
}
func (m *Messenger) ReadActivityCenterNotifications(cursor string, limit uint64, activityType ActivityCenterType) (*ActivityCenterPaginationResponse, error) {
cursor, notifications, err := m.persistence.ReadActivityCenterNotifications(cursor, limit, activityType)
func (m *Messenger) ReadActivityCenterNotifications(cursor string, limit uint64, activityTypes []ActivityCenterType) (*ActivityCenterPaginationResponse, error) {
cursor, notifications, err := m.persistence.ReadActivityCenterNotifications(cursor, limit, activityTypes)
if err != nil {
return nil, err
}
@ -255,8 +259,8 @@ func (m *Messenger) ReadActivityCenterNotifications(cursor string, limit uint64,
}, nil
}
func (m *Messenger) UnreadActivityCenterNotifications(cursor string, limit uint64, activityType ActivityCenterType) (*ActivityCenterPaginationResponse, error) {
cursor, notifications, err := m.persistence.UnreadActivityCenterNotifications(cursor, limit, activityType)
func (m *Messenger) UnreadActivityCenterNotifications(cursor string, limit uint64, activityTypes []ActivityCenterType) (*ActivityCenterPaginationResponse, error) {
cursor, notifications, err := m.persistence.UnreadActivityCenterNotifications(cursor, limit, activityTypes)
if err != nil {
return nil, err
}
@ -267,8 +271,8 @@ func (m *Messenger) UnreadActivityCenterNotifications(cursor string, limit uint6
}, nil
}
func (m *Messenger) ActivityCenterNotificationsBy(cursor string, limit uint64, activityType ActivityCenterType, readType ActivityCenterQueryParamsRead) (*ActivityCenterPaginationResponse, error) {
cursor, notifications, err := m.persistence.ActivityCenterNotificationsBy(cursor, limit, activityType, readType)
func (m *Messenger) ActivityCenterNotificationsBy(cursor string, limit uint64, activityTypes []ActivityCenterType, readType ActivityCenterQueryParamsRead, accepted bool) (*ActivityCenterPaginationResponse, error) {
cursor, notifications, err := m.persistence.ActivityCenterNotificationsBy(cursor, limit, activityTypes, readType, accepted)
if err != nil {
return nil, err
}

View File

@ -2005,9 +2005,10 @@ func (m *Messenger) resumeHistoryArchivesImport(communityID types.HexBytes) erro
m.communitiesManager.AddHistoryArchiveDownloadTask(communityID.String(), task)
go func() {
// this wait groups tracks the ongoing task for a particular community
task.Waiter.Add(1)
go func() {
defer func() {
task.Waiter.Done()
m.communitiesManager.DeleteHistoryArchiveDownloadTask(communityID.String())

View File

@ -535,7 +535,11 @@ func (s *MessengerVerificationRequests) TestDeclineVerificationRequests() {
s.Require().Equal(resp.Messages()[0].ContactVerificationState, common.ContactVerificationStatePending)
// Make sure it's stored and retrieved correctly
notifications, err := theirMessenger.UnreadActivityCenterNotifications("", 4, ActivityCenterNotificationTypeContactVerification)
notifications, err := theirMessenger.UnreadActivityCenterNotifications(
"",
4,
[]ActivityCenterType{ActivityCenterNotificationTypeContactVerification},
)
s.Require().NoError(err)
s.Require().Len(notifications.Notifications, 1)
s.Require().Equal(notifications.Notifications[0].ContactVerificationStatus, verification.RequestStatusPENDING)
@ -561,7 +565,11 @@ func (s *MessengerVerificationRequests) TestDeclineVerificationRequests() {
s.Require().Equal(resp.Messages()[0].ContactVerificationState, common.ContactVerificationStateDeclined)
// Make sure it's stored and retrieved correctly
notifications, err = theirMessenger.UnreadActivityCenterNotifications("", 4, ActivityCenterNotificationTypeContactVerification)
notifications, err = theirMessenger.UnreadActivityCenterNotifications(
"",
4,
[]ActivityCenterType{ActivityCenterNotificationTypeContactVerification},
)
s.Require().NoError(err)
s.Require().Len(notifications.Notifications, 1)
s.Require().Equal(notifications.Notifications[0].ContactVerificationStatus, verification.RequestStatusDECLINED)
@ -629,7 +637,11 @@ func (s *MessengerVerificationRequests) TestCancelVerificationRequest() {
s.Require().Equal(common.ContactVerificationStatePending, resp.Messages()[0].ContactVerificationState)
// Make sure it's stored and retrieved correctly
notifications, err := theirMessenger.UnreadActivityCenterNotifications("", 4, ActivityCenterNotificationTypeContactVerification)
notifications, err := theirMessenger.UnreadActivityCenterNotifications(
"",
4,
[]ActivityCenterType{ActivityCenterNotificationTypeContactVerification},
)
s.Require().NoError(err)
s.Require().Len(notifications.Notifications, 1)
s.Require().Equal(notifications.Notifications[0].ContactVerificationStatus, verification.RequestStatusPENDING)

View File

@ -1486,20 +1486,28 @@ func TestActivityCenterReadUnread(t *testing.T) {
err = p.MarkActivityCenterNotificationsRead([]types.HexBytes{nID2})
require.NoError(t, err)
cursor, notifications, err := p.UnreadActivityCenterNotifications("", 2, ActivityCenterNotificationTypeNewOneToOne)
cursor, notifications, err := p.UnreadActivityCenterNotifications(
"",
2,
[]ActivityCenterType{ActivityCenterNotificationTypeNewOneToOne},
)
require.NoError(t, err)
require.Empty(t, cursor)
require.Len(t, notifications, 1)
require.Equal(t, nID1, notifications[0].ID)
cursor, notifications, err = p.ReadActivityCenterNotifications("", 2, ActivityCenterNotificationTypeNewOneToOne)
cursor, notifications, err = p.ReadActivityCenterNotifications(
"",
2,
[]ActivityCenterType{ActivityCenterNotificationTypeNewOneToOne},
)
require.NoError(t, err)
require.Empty(t, cursor)
require.Len(t, notifications, 1)
require.Equal(t, nID2, notifications[0].ID)
}
func TestActivityCenterReadUnreadFilterByType(t *testing.T) {
func TestActivityCenterReadUnreadFilterByTypes(t *testing.T) {
db, err := openTestDB()
require.NoError(t, err)
p := newSQLitePersistence(db)
@ -1544,29 +1552,68 @@ func TestActivityCenterReadUnreadFilterByType(t *testing.T) {
require.NoError(t, err)
}
_, notifications, err := p.UnreadActivityCenterNotifications(initialCursor, limit, ActivityCenterNotificationTypeNewOneToOne)
// Don't filter by type if the array of types is empty.
_, notifications, err := p.UnreadActivityCenterNotifications(
initialCursor,
limit,
[]ActivityCenterType{},
)
require.NoError(t, err)
require.Len(t, notifications, 3)
require.Equal(t, nID3, notifications[0].ID)
require.Equal(t, nID2, notifications[1].ID)
require.Equal(t, nID1, notifications[2].ID)
_, notifications, err = p.UnreadActivityCenterNotifications(
initialCursor,
limit,
[]ActivityCenterType{ActivityCenterNotificationTypeNewOneToOne},
)
require.NoError(t, err)
require.Len(t, notifications, 1)
require.Equal(t, nID2, notifications[0].ID)
_, notifications, err = p.UnreadActivityCenterNotifications(initialCursor, limit, ActivityCenterNotificationTypeMention)
_, notifications, err = p.UnreadActivityCenterNotifications(
initialCursor,
limit,
[]ActivityCenterType{ActivityCenterNotificationTypeMention},
)
require.NoError(t, err)
require.Len(t, notifications, 2)
require.Equal(t, nID3, notifications[0].ID)
require.Equal(t, nID1, notifications[1].ID)
_, notifications, err = p.UnreadActivityCenterNotifications(
initialCursor,
limit,
[]ActivityCenterType{ActivityCenterNotificationTypeMention, ActivityCenterNotificationTypeNewOneToOne},
)
require.NoError(t, err)
require.Len(t, notifications, 3)
require.Equal(t, nID3, notifications[0].ID)
require.Equal(t, nID2, notifications[1].ID)
require.Equal(t, nID1, notifications[2].ID)
// Mark all notifications as read.
for _, notification := range allNotifications {
err = p.MarkActivityCenterNotificationsRead([]types.HexBytes{notification.ID})
require.NoError(t, err)
}
_, notifications, err = p.ReadActivityCenterNotifications(initialCursor, limit, ActivityCenterNotificationTypeNewOneToOne)
_, notifications, err = p.ReadActivityCenterNotifications(
initialCursor,
limit,
[]ActivityCenterType{ActivityCenterNotificationTypeNewOneToOne},
)
require.NoError(t, err)
require.Len(t, notifications, 1)
require.Equal(t, nID2, notifications[0].ID)
_, notifications, err = p.ReadActivityCenterNotifications(initialCursor, limit, ActivityCenterNotificationTypeMention)
_, notifications, err = p.ReadActivityCenterNotifications(
initialCursor,
limit,
[]ActivityCenterType{ActivityCenterNotificationTypeMention},
)
require.NoError(t, err)
require.Len(t, notifications, 2)
require.Equal(t, nID3, notifications[0].ID)
@ -1638,34 +1685,54 @@ func TestActivityCenterReadUnreadPagination(t *testing.T) {
require.NoError(t, err)
// Fetch UNREAD notifications, first page.
cursor, notifications, err := p.UnreadActivityCenterNotifications(initialOrFinalCursor, 1, ActivityCenterNotificationTypeNewOneToOne)
cursor, notifications, err := p.UnreadActivityCenterNotifications(
initialOrFinalCursor,
1,
[]ActivityCenterType{ActivityCenterNotificationTypeNewOneToOne},
)
require.NoError(t, err)
require.Len(t, notifications, 1)
require.Equal(t, nID5, notifications[0].ID)
require.NotEmpty(t, cursor)
// Fetch next pages.
cursor, notifications, err = p.UnreadActivityCenterNotifications(cursor, 1, ActivityCenterNotificationTypeNewOneToOne)
cursor, notifications, err = p.UnreadActivityCenterNotifications(
cursor,
1,
[]ActivityCenterType{ActivityCenterNotificationTypeNewOneToOne},
)
require.NoError(t, err)
require.Len(t, notifications, 1)
require.Equal(t, nID3, notifications[0].ID)
require.NotEmpty(t, cursor)
cursor, notifications, err = p.UnreadActivityCenterNotifications(cursor, 1, ActivityCenterNotificationTypeNewOneToOne)
cursor, notifications, err = p.UnreadActivityCenterNotifications(
cursor,
1,
[]ActivityCenterType{ActivityCenterNotificationTypeNewOneToOne},
)
require.NoError(t, err)
require.Len(t, notifications, 1)
require.Equal(t, nID1, notifications[0].ID)
require.Empty(t, cursor)
// Fetch READ notifications, first page.
cursor, notifications, err = p.ReadActivityCenterNotifications(initialOrFinalCursor, 1, ActivityCenterNotificationTypeNewOneToOne)
cursor, notifications, err = p.ReadActivityCenterNotifications(
initialOrFinalCursor,
1,
[]ActivityCenterType{ActivityCenterNotificationTypeNewOneToOne},
)
require.NoError(t, err)
require.Len(t, notifications, 1)
require.Equal(t, nID4, notifications[0].ID)
require.NotEmpty(t, cursor)
// Fetch next page.
cursor, notifications, err = p.ReadActivityCenterNotifications(cursor, 1, ActivityCenterNotificationTypeNewOneToOne)
cursor, notifications, err = p.ReadActivityCenterNotifications(
cursor,
1,
[]ActivityCenterType{ActivityCenterNotificationTypeNewOneToOne},
)
require.NoError(t, err)
require.Len(t, notifications, 1)
require.Equal(t, nID2, notifications[0].ID)

View File

@ -31,7 +31,6 @@ import (
"github.com/status-im/status-go/contracts/resolver"
"github.com/status-im/status-go/contracts/snt"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/multiaccounts/accounts"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/rpc"
"github.com/status-im/status-go/services/rpcfilters"
@ -74,7 +73,6 @@ type API struct {
quit chan struct{}
db *Database
settings *accounts.Database // NOTE: Accounts db is here for compatibility. Remove when settings.usernames is dropped.
}
func (api *API) Stop() {

View File

@ -1082,6 +1082,10 @@ func (api *PublicAPI) UnreadActivityCenterNotificationsCount() (uint64, error) {
return api.service.messenger.UnreadActivityCenterNotificationsCount()
}
func (api *PublicAPI) UnreadAndAcceptedActivityCenterNotificationsCount() (uint64, error) {
return api.service.messenger.UnreadAndAcceptedActivityCenterNotificationsCount()
}
func (api *PublicAPI) MarkAllActivityCenterNotificationsRead(ctx context.Context) error {
return api.service.messenger.MarkAllActivityCenterNotificationsRead(ctx)
}
@ -1117,15 +1121,15 @@ func (api *PublicAPI) ActivityCenterNotifications(cursor string, limit uint64) (
}
func (api *PublicAPI) ReadActivityCenterNotifications(cursor string, limit uint64, activityType protocol.ActivityCenterType) (*protocol.ActivityCenterPaginationResponse, error) {
return api.service.messenger.ReadActivityCenterNotifications(cursor, limit, activityType)
return api.service.messenger.ReadActivityCenterNotifications(cursor, limit, []protocol.ActivityCenterType{activityType})
}
func (api *PublicAPI) UnreadActivityCenterNotifications(cursor string, limit uint64, activityType protocol.ActivityCenterType) (*protocol.ActivityCenterPaginationResponse, error) {
return api.service.messenger.UnreadActivityCenterNotifications(cursor, limit, activityType)
return api.service.messenger.UnreadActivityCenterNotifications(cursor, limit, []protocol.ActivityCenterType{activityType})
}
func (api *PublicAPI) ActivityCenterNotificationsBy(cursor string, limit uint64, activityType protocol.ActivityCenterType, readType protocol.ActivityCenterQueryParamsRead) (*protocol.ActivityCenterPaginationResponse, error) {
return api.service.messenger.ActivityCenterNotificationsBy(cursor, limit, activityType, readType)
func (api *PublicAPI) ActivityCenterNotificationsBy(cursor string, limit uint64, activityTypes []protocol.ActivityCenterType, readType protocol.ActivityCenterQueryParamsRead, accepted bool) (*protocol.ActivityCenterPaginationResponse, error) {
return api.service.messenger.ActivityCenterNotificationsBy(cursor, limit, activityTypes, readType, accepted)
}
func (api *PublicAPI) RequestAllHistoricMessages() (*protocol.MessengerResponse, error) {

View File

@ -34,7 +34,7 @@ pkgs.mkShell {
buildInputs = with pkgs; [
git jq which
go_1_18 gopls go-bindata
go_1_18 golangci-lint gopls go-bindata
mockgen protobuf3_17 protoc-gen-go
(gomobile.override { xcodeWrapperArgs = { version = "13.4.1"; }; })
] ++ pkgs.lib.optionals pkgs.stdenv.isDarwin [