The content length message was making the page jumpy and causing reflows
in three ways. This commit addresses each of those ways:
As the user scrolled
---
The footer went from fixed to sticky and the spacing around the message
changed. This change in spacing was needed so that the message looked
right in both contexts.
I think the best way to resolve this is to not use the sticky footer
when editing text message or broadcast templates.
On my 1440×900 screen I can fit a 5 fragment text message, plus the
‘will be charged as 5 text messages’ message, plus the save button.
Our top 10 screen resolutions according to our analytics are:
Position | Resolution | Percentage of users
---------|------------|--------------------
1 | 1920x1080 | 27.37%
2 | 1280×720 | 11.07%
3 | 1366×768 | 8.88%
4 | 1536×864 | 5.79%
5 | 1440×900 | 4.52%
6 | 1600×900 | 3.71%
7 | 1280×1024 | 3.10%
8 | 1680×1050 | 2.42%
9 | 1920×1200 | 2.33%
10 | 2560×1440 | 1.99%
When the page first loaded
---
The message is empty so takes up no space, then the javascript fires
and inserts the message, taking up a line of space.
This is resolved by making the empty message take up space with a
non-breaking space character.
When the user first typed
---
We previously didn’t show any message until the user started typing.
This meant that, with the above fix, there was a larger than normal
empty space between the textarea and the save button.
This is resolved by always showing the message, even when the user
hasn’t typed anything yet.
***
These are design decisions which made sense when the message was
displayed along side the button, but we’ve had to change now that the
message is above the button.
Users sending text messages are sometimes unaware that long messages
will cost more.
Users sending broadcast messages need to be aware that there’s a
character limit, so they can take this into account when planning their
messages.
This commit adds an endpoint which counts the number of characters in
some template content, and returns a snippet of useful info about how
long the message is.
In subsequent commits we’ll be able to use AJAX to fetch this snippet as
the user types.
There’s a surprising amount of complexity in counting the length of
messages. So we’ll need to do this in Python because it would be too
convoluted to re-implement the length counting in client side code, let
alone ensuring it had parity with its Python equivalent.
We have lots of functions for converting various types of data into
strings to be displayed to the user somewhere.
This commit collects all these functions into their own module, rather
than having them cluttering up `app/__init__.py` or buried amongst
various other things that have ended up in `app/utils.py`.
`app/utils.py` is a bit of a dumping ground for things we don’t have a
better place for.
We now have a place and structure for storing ‘model’ code (‘model’ in
the model, view, controller (MVC) sense of the word).
This commit moves the spreadsheet model to that place.
We shouldn’t have a page where someone can look up any other user’s
email address based on their user ID.
We also don’t want a page where a malicious user could send someone an
link which would get them invited to the service.
Restricting the invite to be populated just from users in their own
organisation doesn’t mitigate against this stuff completely, but they
probably have a way of finding out the email address of someone in their
organisation already.
At the moment users must be invited to join a service. But this means:
- users must know that a service already exists
- they need to know who to ask for an invite
If the user doesn’t know these thing then sometimes they just go ahead
and set up a new service. Which means they have to get all the way to
the point of requesting to go live before we tell them that there’s
already a service with a similar name or purpose.
So we should let users:
1. discover what other services exist in their organisation
2. apply to join a service
3. automatically notify the service managers of their interest
4. be invited by a service manager
5. accept the invite
This commit implements step 4. We can just link them to the invite form
in step 3., but we should make it easy for them to send the invite,
without having to copy and paste email addresses.
So this commit let the invite form be pre-populated with an existing
user’s email address.
It’s one of the things we check when someone makes a request to go live,
and putting it in the ticket means we don’t have to take the extra step
of clicking into the settings.
Also added some line breaks to chunk things up a bit more clearly.
When we get a support ticket we need to check whether a user has any
live services.
We have a method for this on the user model now, so we don’t need a
separate function in the feedback code.
It wasn’t very well tested so I’ve adapted the old tests from the
feedback view to work against the method on the user model too.
Includes changing form.enabled to use
OnOffField, for consistency with other on/off
fields.
OnOffField's data is a boolean, not a string, so
some of the logic using it needed to be changed.
On the uploads page we only show jobs which are within a service’s data
retention.
This commit does the same for when we’re listing the jobs for a contact
list. This matches the UI, which says a contact list has been ‘used
`<count_of_jobs>` in the last <data_retention> days’
It’s a bit unintuitive that starting a job from a contact list makes a
copy of the file, which has no relationship to the list it was copied
from. This is more of an implementation detail, rather than something
that comes from people’s mental models of what is going on. Or at least
that’s what I hypothesise.
I think it’s clearer to show jobs that come from contact lists within
the lists that they were created from. By naming the jobs by template
this gives a clearer view of what messages have been sent to the group
over time.
Live services shouldn't be able to request to go live again. Once a
service is live we remove the option to go live from the Settings page,
but we still link to the page to request to go live from other places
e.g. the 'Get started' page. As a result, we've seen some services make
another request to go live when their service has already been live for
months - this change will stop that from happening.
We look for `original_file_name` in the metadata now. Initially we were
still checking the query string too, but now that the change to add the
filename to the metadata has been deployed for a while there shouldn't
be any cases of the filename still being in a query string.
Since the `original_file_name` is not being added to the metadata in
`.check_messages` (it has happened earlier in the process) a few tests
are no longer needed.
The code was looking for `original_file_name` in the metadata for a
contact list, or the query string if it wasn't in the metadata. Now that
the change to use the metadata for the file name has been deployed for a
while e can stop looking in the query string for the
`original_file_name`.
When sending from an uploaded CSV `.send_messages` now puts the filename
in the metadata. It previously used the query string to pass the
filename to `.check_messages`, where it can be lost.
The `.send_from_contact_list` function redirected to `.check_messages`
with `original_file_name` in the query string. Contact lists already
have `original_file_name` as part of their metadata, so we can stop
sending it in the query string and use the metadata instead.
We were passing `original_file_name` from the `.upload_contact_list`
view function to the `.check_contact_list` view function as a query
param. We now store it in the metadata instead. `.check_contact_list`
still checks for `original_file_name` in the query string if it's not in
the metadata - this is necessary until the code has been deployed for a
few days and we can be sure that there are no contact lists that are
mid-way through the upload stage.
Splitting the dashboard into multiple sections was confusing, and people
sometimes mistook the headings as labels, especially when a section was
empty. It just wasn’t clear what the hierarchy of the page was.
This commit combines the current and pending broadcasts into one list
on the dashboard. Previous broadcasts have already moved to their own
page.
If you refresh the page on a current broadcast while someone has
cancelled it you’ll see the wrong navigation item selected. This commit
adds redirects to take you to the correct endpoint in these edge cases.
Once a broadcast has been submitted for approval it either lives on the
‘Current alerts’ or ‘Previous alerts’ page, depending on where it is
in its lifecycle.
Therefore when clicking into a broadcast from one of those pages the
same navigation item should remain selected.
Because we select the navigation items based on the request endpoint,
this means we need an endpoint for each navigation page, even if the
content of the pages will be the same in both cases.
This commit adds the two new end points, removes the old, single
endpoint and updates links to point to the new endpoint.
The idea was that this would be a place to document all the design
patterns used in Notify. However it hasn’t been kept up to date, and,
looking at the `git blame`[1] no new patterns have been added for 5
years.
I think it’s better to get rid of it than have to keep maintaining
something which is inaccurate.
1. 64aa0d359c/app/templates/views/styleguide.html
The dashboard for normal services is quite general, because it tells
you a bit about channels, templates and spend.
What is now the dashboard for broadcast services is much more specific,
therefore less like a dashboard. We can reflect this by giving it a more
specific name. This should reduce the amount of navigation surfing
people need to do in order to find the thing they’re looking for.
Previous alerts are much less important than ones that are live or
waiting for approval.
Therefore we can make the dashboard more focused by moving previous
alerts to their own page.
We don't need these anymore as all users will use the `one-off/step`
routes.
This has mostly involved tidying up the tests which are still a little
disorganised and not as good as I'd like but it's a step in the right
direction.
More refactoring is still possible to the routes, it may come in a later
PR if I have time.
When we add a new property to the broadcast model, we need to delete any
cached broadcasts from Redis that are missing the new property. So this
adds an option to do this to the cache page in platform admin.
I’ve also tried to make it more obvious what the magic numbers in the
test fixture are doing.