Changeset 781 in code for trunk/db_sqlite.go


Ignore:
Timestamp:
Feb 11, 2022, 6:41:46 PM (3 years ago)
Author:
delthas
Message:

Add support for the wip soju.im/read capability and READ command

READ lets downstream clients share information between each other about
what messages have been read by other downstreams.

Each target/entity has an optional corresponding read receipt, which is
stored as a timestamp.

  • When a downstream sends: READ #chan timestamp=2020-01-01T01:23:45.000Z the read receipt for that target is set to that date
  • soju sends READ to downstreams:
    • on JOIN, if the client uses the soju.im/read capability
    • when the read receipt timestamp is set by any downstream

The read receipt date is clamped by the previous receipt date and the
current time.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/db_sqlite.go

    r712 r781  
    7070        FOREIGN KEY(network) REFERENCES Network(id),
    7171        UNIQUE(network, target, client)
     72);
     73
     74CREATE TABLE ReadReceipt (
     75        id INTEGER PRIMARY KEY,
     76        network INTEGER NOT NULL,
     77        target TEXT NOT NULL,
     78        timestamp TEXT NOT NULL,
     79        FOREIGN KEY(network) REFERENCES Network(id),
     80        UNIQUE(network, target)
    7281);
    7382`
     
    171180                ALTER TABLE NetworkNew RENAME TO Network;
    172181        `,
     182        `
     183                CREATE TABLE ReadReceipt (
     184                        id INTEGER PRIMARY KEY,
     185                        network INTEGER NOT NULL,
     186                        target TEXT NOT NULL,
     187                        timestamp TEXT NOT NULL,
     188                        FOREIGN KEY(network) REFERENCES Network(id),
     189                        UNIQUE(network, target)
     190                );
     191        `,
    173192}
    174193
     
    378397                        FROM DeliveryReceipt
    379398                        JOIN Network ON DeliveryReceipt.network = Network.id
     399                        WHERE Network.user = ?
     400                )`, id)
     401        if err != nil {
     402                return err
     403        }
     404
     405        _, err = tx.ExecContext(ctx, `DELETE FROM ReadReceipt
     406                WHERE id IN (
     407                        SELECT ReadReceipt.id
     408                        FROM ReadReceipt
     409                        JOIN Network ON ReadReceipt.network = Network.id
    380410                        WHERE Network.user = ?
    381411                )`, id)
     
    546576        }
    547577
     578        _, err = tx.ExecContext(ctx, "DELETE FROM ReadReceipt WHERE network = ?", id)
     579        if err != nil {
     580                return err
     581        }
     582
    548583        _, err = tx.ExecContext(ctx, "DELETE FROM Channel WHERE network = ?", id)
    549584        if err != nil {
     
    720755        return tx.Commit()
    721756}
     757
     758func (db *SqliteDB) GetReadReceipt(ctx context.Context, networkID int64, name string) (*ReadReceipt, error) {
     759        db.lock.RLock()
     760        defer db.lock.RUnlock()
     761
     762        ctx, cancel := context.WithTimeout(ctx, sqliteQueryTimeout)
     763        defer cancel()
     764
     765        receipt := &ReadReceipt{
     766                Target: name,
     767        }
     768
     769        row := db.db.QueryRowContext(ctx, `
     770                SELECT id, timestamp FROM ReadReceipt WHERE network = :network AND target = :target`,
     771                sql.Named("network", networkID),
     772                sql.Named("target", name),
     773        )
     774        var timestamp string
     775        if err := row.Scan(&receipt.ID, &timestamp); err != nil {
     776                if err == sql.ErrNoRows {
     777                        return nil, nil
     778                }
     779                return nil, err
     780        }
     781        if t, err := time.Parse(serverTimeLayout, timestamp); err != nil {
     782                return nil, err
     783        } else {
     784                receipt.Timestamp = t
     785        }
     786        return receipt, nil
     787}
     788
     789func (db *SqliteDB) StoreReadReceipt(ctx context.Context, networkID int64, receipt *ReadReceipt) error {
     790        db.lock.Lock()
     791        defer db.lock.Unlock()
     792
     793        ctx, cancel := context.WithTimeout(ctx, sqliteQueryTimeout)
     794        defer cancel()
     795
     796        args := []interface{}{
     797                sql.Named("id", receipt.ID),
     798                sql.Named("timestamp", receipt.Timestamp.UTC().Format(serverTimeLayout)),
     799                sql.Named("network", networkID),
     800                sql.Named("target", receipt.Target),
     801        }
     802
     803        var err error
     804        if receipt.ID != 0 {
     805                _, err = db.db.ExecContext(ctx, `
     806                        UPDATE ReadReceipt SET timestamp = :timestamp WHERE id = :id`,
     807                        args...)
     808        } else {
     809                var res sql.Result
     810                res, err = db.db.ExecContext(ctx, `
     811                        INSERT INTO
     812                        ReadReceipt(network, target, timestamp)
     813                        VALUES (:network, :target, :timestamp)`,
     814                        args...)
     815                if err != nil {
     816                        return err
     817                }
     818                receipt.ID, err = res.LastInsertId()
     819        }
     820
     821        return err
     822}
Note: See TracChangeset for help on using the changeset viewer.