SendMessageResults sendEndSessionMessage(Set<RecipientIdentifier.Single> recipients) throws IOException;
+ void hideRecipient(RecipientIdentifier.Single recipient);
+
void deleteRecipient(RecipientIdentifier.Single recipient);
void deleteContact(RecipientIdentifier.Single recipient);
int messageExpirationTime,
boolean isBlocked,
boolean isArchived,
- boolean isProfileSharingEnabled
+ boolean isProfileSharingEnabled,
+ boolean isHidden
) {
private Contact(final Builder builder) {
builder.familyName,
builder.color,
builder.messageExpirationTime,
- builder.blocked,
- builder.archived,
- builder.profileSharingEnabled);
+ builder.isBlocked,
+ builder.isArchived,
+ builder.isProfileSharingEnabled,
+ builder.isHidden);
}
public static Builder newBuilder() {
builder.familyName = copy.familyName();
builder.color = copy.color();
builder.messageExpirationTime = copy.messageExpirationTime();
- builder.blocked = copy.isBlocked();
- builder.archived = copy.isArchived();
- builder.profileSharingEnabled = copy.isProfileSharingEnabled();
+ builder.isBlocked = copy.isBlocked();
+ builder.isArchived = copy.isArchived();
+ builder.isProfileSharingEnabled = copy.isProfileSharingEnabled();
+ builder.isHidden = copy.isHidden();
return builder;
}
private String familyName;
private String color;
private int messageExpirationTime;
- private boolean blocked;
- private boolean archived;
- private boolean profileSharingEnabled;
+ private boolean isBlocked;
+ private boolean isArchived;
+ private boolean isProfileSharingEnabled;
+ private boolean isHidden;
private Builder() {
}
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
public Builder withGivenName(final String val) {
givenName = val;
return this;
return this;
}
- public Builder withBlocked(final boolean val) {
- blocked = val;
+ public Builder withIsBlocked(final boolean val) {
+ isBlocked = val;
+ return this;
+ }
+
+ public Builder withIsArchived(final boolean val) {
+ isArchived = val;
return this;
}
- public Builder withArchived(final boolean val) {
- archived = val;
+ public Builder withIsProfileSharingEnabled(final boolean val) {
+ isProfileSharingEnabled = val;
return this;
}
- public Builder withProfileSharingEnabled(final boolean val) {
- profileSharingEnabled = val;
+ public Builder withIsHidden(final boolean val) {
+ isHidden = val;
return this;
}
public void setContactName(final RecipientId recipientId, final String givenName, final String familyName) {
var contact = account.getContactStore().getContact(recipientId);
final var builder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact);
+ builder.withIsHidden(false);
if (givenName != null) {
builder.withGivenName(givenName);
}
var contact = account.getContactStore().getContact(recipientId);
final var builder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact);
if (blocked) {
- builder.withProfileSharingEnabled(false);
+ builder.withIsProfileSharingEnabled(false);
}
- account.getContactStore().storeContact(recipientId, builder.withBlocked(blocked).build());
+ account.getContactStore().storeContact(recipientId, builder.withIsBlocked(blocked).build());
+ }
+
+ public void setContactHidden(RecipientId recipientId, boolean hidden) {
+ var contact = account.getContactStore().getContact(recipientId);
+ final var builder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact);
+ account.getContactStore().storeContact(recipientId, builder.withIsHidden(hidden).build());
}
}
Optional<Long> editTargetTimestamp
) {
var contact = account.getContactStore().getContact(recipientId);
- if (contact == null || !contact.isProfileSharingEnabled()) {
+ if (contact == null || !contact.isProfileSharingEnabled() || contact.isHidden()) {
final var contactBuilder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact);
- contact = contactBuilder.withProfileSharingEnabled(true).build();
+ contact = contactBuilder.withIsProfileSharingEnabled(true).withIsHidden(false).build();
account.getContactStore().storeContact(recipientId, contact);
}
final var blocked = contact != null && contact.isBlocked();
final var profileShared = contact != null && contact.isProfileSharingEnabled();
final var archived = contact != null && contact.isArchived();
+ final var hidden = contact != null && contact.isHidden();
final var contactGivenName = contact == null ? null : contact.givenName();
final var contactFamilyName = contact == null ? null : contact.familyName();
if (blocked != contactRecord.isBlocked()
|| profileShared != contactRecord.isProfileSharingEnabled()
|| archived != contactRecord.isArchived()
+ || hidden != contactRecord.isHidden()
|| (
contactRecord.getSystemGivenName().isPresent() && !contactRecord.getSystemGivenName()
.get()
)) {
logger.debug("Storing new or updated contact {}", recipientId);
final var contactBuilder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact);
- final var newContact = contactBuilder.withBlocked(contactRecord.isBlocked())
- .withProfileSharingEnabled(contactRecord.isProfileSharingEnabled())
- .withArchived(contactRecord.isArchived());
+ final var newContact = contactBuilder.withIsBlocked(contactRecord.isBlocked())
+ .withIsProfileSharingEnabled(contactRecord.isProfileSharingEnabled())
+ .withIsArchived(contactRecord.isArchived())
+ .withIsHidden(contactRecord.isHidden());
if (contactRecord.getSystemGivenName().isPresent() || contactRecord.getSystemFamilyName().isPresent()) {
newContact.withGivenName(contactRecord.getSystemGivenName().orElse(null))
.withFamilyName(contactRecord.getSystemFamilyName().orElse(null));
if (c.getExpirationTimer().isPresent()) {
builder.withMessageExpirationTime(c.getExpirationTimer().get());
}
- builder.withBlocked(c.isBlocked());
- builder.withArchived(c.isArchived());
+ builder.withIsBlocked(c.isBlocked());
+ builder.withIsArchived(c.isArchived());
account.getContactStore().storeContact(recipientId, builder.build());
if (c.getAvatar().isPresent()) {
}
}
+ @Override
+ public void hideRecipient(final RecipientIdentifier.Single recipient) {
+ final var recipientIdOptional = context.getRecipientHelper().resolveRecipientOptional(recipient);
+ if (recipientIdOptional.isPresent()) {
+ context.getContactHelper().setContactHidden(recipientIdOptional.get(), true);
+ account.removeRecipient(recipientIdOptional.get());
+ }
+ }
+
@Override
public void deleteRecipient(final RecipientIdentifier.Single recipient) {
final var recipientIdOptional = context.getRecipientHelper().resolveRecipientOptional(recipient);
public class AccountDatabase extends Database {
private static final Logger logger = LoggerFactory.getLogger(AccountDatabase.class);
- private static final long DATABASE_VERSION = 18;
+ private static final long DATABASE_VERSION = 19;
private AccountDatabase(final HikariDataSource dataSource) {
super(logger, DATABASE_VERSION, dataSource);
""");
}
}
+ if (oldVersion < 19) {
+ logger.debug("Updating database: Adding contact hidden column");
+ try (final var statement = connection.createStatement()) {
+ statement.executeUpdate("""
+ ALTER TABLE recipient ADD COLUMN hidden INTEGER NOT NULL DEFAULT FALSE;
+ """);
+ }
+ }
}
}
contact.messageExpirationTime,
contact.blocked,
contact.archived,
+ false,
false));
// Store profile keys only in profile store
r.contact.messageExpirationTime,
r.contact.blocked,
r.contact.archived,
- r.contact.profileSharingEnabled);
+ r.contact.profileSharingEnabled,
+ false);
}
ProfileKey profileKey = null;
blocked INTEGER NOT NULL DEFAULT FALSE,
archived INTEGER NOT NULL DEFAULT FALSE,
profile_sharing INTEGER NOT NULL DEFAULT FALSE,
+ hidden INTEGER NOT NULL DEFAULT FALSE,
profile_last_update_timestamp INTEGER NOT NULL DEFAULT 0,
profile_given_name TEXT,
public List<Pair<RecipientId, Contact>> getContacts() {
final var sql = (
"""
- SELECT r._id, r.given_name, r.family_name, r.expiration_time, r.profile_sharing, r.color, r.blocked, r.archived
+ SELECT r._id, r.given_name, r.family_name, r.expiration_time, r.profile_sharing, r.color, r.blocked, r.archived, r.hidden
FROM %s r
- WHERE (r.number IS NOT NULL OR r.uuid IS NOT NULL) AND %s
+ WHERE (r.number IS NOT NULL OR r.uuid IS NOT NULL) AND %s AND r.hidden = FALSE
"""
).formatted(TABLE_RECIPIENT, SQL_IS_CONTACT);
try (final var connection = database.getConnection()) {
final var sqlWhere = new ArrayList<String>();
if (onlyContacts) {
sqlWhere.add("(" + SQL_IS_CONTACT + ")");
+ sqlWhere.add("r.hidden = FALSE");
}
if (blocked.isPresent()) {
sqlWhere.add("r.blocked = ?");
SELECT r._id,
r.number, r.uuid, r.pni, r.username,
r.profile_key, r.profile_key_credential,
- r.given_name, r.family_name, r.expiration_time, r.profile_sharing, r.color, r.blocked, r.archived,
+ r.given_name, r.family_name, r.expiration_time, r.profile_sharing, r.color, r.blocked, r.archived, r.hidden,
r.profile_last_update_timestamp, r.profile_given_name, r.profile_family_name, r.profile_about, r.profile_about_emoji, r.profile_avatar_url_path, r.profile_mobile_coin_address, r.profile_unidentified_access_mode, r.profile_capabilities
FROM %s r
WHERE (r.number IS NOT NULL OR r.uuid IS NOT NULL) AND %s
private Contact getContact(final Connection connection, final RecipientId recipientId) throws SQLException {
final var sql = (
"""
- SELECT r.given_name, r.family_name, r.expiration_time, r.profile_sharing, r.color, r.blocked, r.archived
+ SELECT r.given_name, r.family_name, r.expiration_time, r.profile_sharing, r.color, r.blocked, r.archived, r.hidden
FROM %s r
WHERE r._id = ? AND (%s)
"""
resultSet.getInt("expiration_time"),
resultSet.getBoolean("blocked"),
resultSet.getBoolean("archived"),
- resultSet.getBoolean("profile_sharing"));
+ resultSet.getBoolean("profile_sharing"),
+ resultSet.getBoolean("hidden"));
}
private Profile getProfileFromResultSet(ResultSet resultSet) throws SQLException {
NUMBER::
Specify the contact phone number.
+*--hide*::
+Hide the contact in the contact list, but keep the data.
+
*--forget*::
Delete all data associated with this contact, including identity keys and sessions.
public void attachToSubparser(final Subparser subparser) {
subparser.help("Remove the details of a given contact");
subparser.addArgument("recipient").help("Contact number");
- subparser.addArgument("--forget")
+ final var mut = subparser.addMutuallyExclusiveGroup();
+ mut.addArgument("--hide")
+ .action(Arguments.storeTrue())
+ .help("Hide the contact in the contact list, but keep the data.");
+ mut.addArgument("--forget")
.action(Arguments.storeTrue())
.help("Delete all data associated with this contact, including identity keys and sessions.");
}
var recipientString = ns.getString("recipient");
var recipient = CommandUtil.getSingleRecipientIdentifier(recipientString, m.getSelfNumber());
+ var hide = Boolean.TRUE == ns.getBoolean("hide");
var forget = Boolean.TRUE == ns.getBoolean("forget");
- if (forget) {
+ if (hide) {
+ m.hideRecipient(recipient);
+ } else if (forget) {
m.deleteRecipient(recipient);
} else {
m.deleteContact(recipient);
return new SendMessageResults(0, Map.of());
}
+ public void hideRecipient(final RecipientIdentifier.Single recipient) {
+ throw new UnsupportedOperationException();
+ }
+
@Override
public void deleteRecipient(final RecipientIdentifier.Single recipient) {
signal.deleteRecipient(recipient.getIdentifier());
}
return Recipient.newBuilder()
.withAddress(new RecipientAddress(null, n))
- .withContact(new Contact(contactName, null, null, 0, contactBlocked, false, false))
+ .withContact(new Contact(contactName, null, null, 0, contactBlocked, false, false, false))
.build();
}).filter(Objects::nonNull).toList();
}